Skip to content

sync: pull launchbadge/sqlx up to 6956cef0; port skip-migrations to MSSQL#4269

Closed
pabl-o-ce wants to merge 37 commits into
launchbadge:mainfrom
pabl-o-ce:sync/upstream-2026-05
Closed

sync: pull launchbadge/sqlx up to 6956cef0; port skip-migrations to MSSQL#4269
pabl-o-ce wants to merge 37 commits into
launchbadge:mainfrom
pabl-o-ce:sync/upstream-2026-05

Conversation

@pabl-o-ce
Copy link
Copy Markdown

@pabl-o-ce pabl-o-ce commented May 17, 2026

No description provided.

Introduces a new `sqlx-mssql` crate that wraps the tiberius TDS protocol
driver (v0.12) behind sqlx's trait system, enabling SQL Server connectivity
with the same ergonomic API as the existing MySQL, PostgreSQL, and SQLite
drivers.

Key implementation details:
- Full Database/Connection/Executor/Row/Value trait implementations
- Connection via tiberius::Client with runtime-agnostic socket bridging
- Eager result collection (tiberius QueryStream borrows &mut Client)
- Transaction support with MSSQL-specific savepoint syntax
- Type mappings: bool, u8, i8, i16, i32, i64, f32, f64, String, Vec<u8>
- Any driver integration and migration support
- Test infrastructure with TestSupport for #[sqlx::test]
- sp_describe_first_result_set for statement metadata and nullability
- Fixes to existing MSSQL test files (imports, placeholders, lifetimes)

Author: Pablo Carrera <pabloce@poscye.com>
Enable native chrono type handling for MSSQL datetime columns,
eliminating the need for CONVERT(VARCHAR) workarounds. Supports
NaiveDateTime, NaiveDate, NaiveTime, and DateTime<Utc> by converting
between tiberius internal datetime structs and chrono types.

Author: Pablo Carrera <pabloce@poscye.com>
Enable UNIQUEIDENTIFIER columns via uuid::Uuid and DECIMAL/NUMERIC/MONEY
columns via rust_decimal::Decimal, both feature-gated behind their
respective cargo features.

Author: Pablo Carrera <pabloce@poscye.com>
Wire up tiberius/time and tiberius/bigdecimal feature flags and implement
Type/Encode/Decode for time::{Date, Time, PrimitiveDateTime, OffsetDateTime},
bigdecimal::BigDecimal, and Json<T>. Uses a ColumnDataWrapper newtype to
bridge tiberius's IntoSql gap for time types and BigDecimal (version mismatch).
JSON is stored as NVARCHAR since SQL Server has no native JSON column type.

Author: Pablo Carrera <pabloce@poscye.com>
…erives, and test attributes

Add tests for all implemented Encode/Decode types (uuid, chrono, time,
rust_decimal, bigdecimal, json, bytes), error kind mapping, derive macros
(weak enums, transparent types), and #[sqlx::test] attribute with
migrations and fixtures.

Author: Pablo Carrera <pabloce@poscye.com>
…lx-mssql

Add MssqlSslMode with 4 variants (Disabled, LoginOnly, Preferred, Required)
mapping to all tiberius EncryptionLevel options. Add MssqlAdvisoryLock API
using sp_getapplock/sp_releaseapplock for application-level distributed
locking. Enable f64/Decimal/BigDecimal decoding from MONEY/SMALLMONEY columns.

Author: Pablo Carrera <pabloce@poscye.com>
…lx-mssql

Add MssqlIsolationLevel enum with begin_with_isolation() for typed
transaction isolation control. Surface tiberius application_intent
(read-only routing) and trust_cert_ca options in MssqlConnectOptions.
Wrap tiberius BulkLoadRequest as MssqlBulkInsert for high-performance
data loading via the TDS INSERT BULK protocol.

Author: Pablo Carrera <pabloce@poscye.com>
… sqlx-mssql

- Add Windows, Integrated, and AAD token authentication support with
  cfg-gated features matching tiberius (winauth, integrated-auth-gssapi)
- Override format_placeholder to produce @p1, @p2, ... instead of ? so
  QueryBuilder works correctly with MSSQL parameterized queries
- Add MssqlXml newtype wrapper for SQL Server XML columns with Type,
  Encode, and Decode implementations
- Add URL parsing for auth and token query parameters with roundtrip tests

Author: Pablo Carrera <pabloce@poscye.com>
Register MSSQL in the macro system (cfg gate, impl_database_ext,
FOSS_DRIVERS) so query!()/query_as!() work with MSSQL databases.
Add missing Any driver type mappings for NULL, BIT, MONEY, SMALLMONEY,
and DECIMAL/NUMERIC to prevent AnyDriverError at runtime.

Author: Pablo Carrera <pabloce@poscye.com>
…ECIMAL precision for MSSQL

Wire MSSQL into docker-compose and CI workflow, support no_tx migrations
following the Postgres pattern, extract column origin from
sp_describe_first_result_set, map date/time/UUID types to Text in the Any
driver, and preserve DECIMAL precision/scale by using a base_name() helper
for type matching.

Author: Pablo Carrera <pabloce@poscye.com>
…d MSSQL examples

Add MssqlAdvisoryLockGuard with Deref/DerefMut, release_now(), and leak()
methods, mirroring the Postgres advisory lock guard pattern. The guard
logs a warning on drop if not explicitly released, since MSSQL connections
cannot queue deferred commands.

Add chrono::DateTime<FixedOffset> Type/Encode/Decode for DATETIMEOFFSET
columns, preserving timezone offset information instead of converting to
UTC. Existing NaiveDateTime and DateTime<Utc> decoders remain backward
compatible by handling the new internal variant.

Add examples/mssql/todos/ with a CLI app demonstrating basic CRUD
operations using MSSQL parameter syntax and OUTPUT INSERTED.

Author: Pablo Carrera <pabloce@poscye.com>
… _persistent flag for MSSQL

Fix Money4 (SMALLMONEY) being incorrectly mapped to "MONEY" instead of
"SMALLMONEY", enabling the Any driver's existing SMALLMONEY handling.
Add comprehensive type tests (integer edge cases, Unicode, large strings,
binary, NULL, XML, DateTime<FixedOffset>, SMALLMONEY) and integration
tests (multiple result sets, column metadata, error recovery, many
parameters, isolation levels, advisory lock guard).

Author: Pablo Carrera <pabloce@poscye.com>
…ion lock

- Escape `]` and `'` in database/schema names interpolated into DDL
  in migrate.rs and testing/mod.rs to prevent SQL injection
- Add SMALLMONEY to compatible() for rust_decimal and bigdecimal types
- Wrap sp_getapplock in DECLARE/THROW to surface lock failures as SQL errors
- Add compatible() overrides for DATE, TIME, and UNIQUEIDENTIFIER types
- Fix swapped Real/Double null type mappings in Any arguments
- Replace panicking expects with proper Error returns in executor
- Add migration test harness and sample migrations for MSSQL

Author: Pablo Carrera <pabloce@poscye.com>
…alculation

Closes #1

Author: Pablo Carrera <pabloce@poscye.com>
Author: Pablo Carrera <pabloce@poscye.com>
…conversion

Dates before the TDS epoch (0001-01-01) produced negative day counts that
silently wrapped via `as u32`, causing corrupt data or a panic inside
`tiberius::time::Date::new()`. Add `days_since_epoch_to_u32()` helper that
returns `Error::Encode` for negative or out-of-range values, and replace
all four unsafe cast sites.

Closes #2

Author: Pablo Carrera <pabloce@poscye.com>
…validated conversion

Adds offset_minutes_to_i16 helper that validates the offset fits within
SQL Server's -840..=840 minute range, returning Error::Encode instead of
silently truncating. Follows the same pattern as days_since_epoch_to_u32.

Author: Pablo Carrera <pabloce@poscye.com>
Apply bracket-quoting for identifier contexts and single-quote escaping
for string literal contexts across all 6 interpolation sites, consistent
with existing escaping in create_database, drop_database, and
create_schema_if_not_exists.

Closes #4

Author: Pablo Carrera <pabloce@poscye.com>
…_if_not_exists

Split the single `escaped` variable into separate variables for each SQL
quoting context, preventing cross-contamination between bracket-quoted
identifiers ([...]) and single-quoted string literals ('...').

Closes #5

Author: Pablo Carrera <pabloce@poscye.com>
…ta with Result propagation

Return Result<MssqlData, Error> instead of panicking on invalid data from
tiberius. Fixes and_local_timezone().unwrap() panic on ambiguous/invalid
DateTimeOffset, and guards against silent u8 truncation in
time_from_sec_fragments with an upfront bounds check. All helper functions
(chrono_date_from_days, time_date_from_days, time_from_sec_fragments) now
return Result and use checked arithmetic.

Author: Pablo Carrera <pabloce@poscye.com>
…terized queries + QUOTENAME()

Replace fragile client-side .replace() escaping in create_database,
drop_database, and create_schema_if_not_exists with parameterized
queries (@p1) and SQL Server's built-in QUOTENAME() function for
server-side identifier escaping via sp_executesql.

Author: Pablo Carrera <pabloce@poscye.com>
…column_data_to_mssql_data

The wildcard `_ => Ok(MssqlData::Null)` silently coerced unhandled
`Some(...)` variants (e.g. Xml, Guid without uuid, Numeric without
rust_decimal) to NULL, causing silent data loss. Enumerate all 18
`None` variants explicitly and error on unhandled `Some(...)` values.

Author: Pablo Carrera <pabloce@poscye.com>
…ation

Closes #9

Author: Pablo Carrera <pabloce@poscye.com>
…check

Extract bigdecimal_to_numeric() helper that normalizes negative exponents
via with_scale(0) (matching tiberius's own to_sql! pattern) and tightens
the scale limit from >38 to >37 to match tiberius's assert!(scale < 38).

Add 10 unit tests covering negative exponents, boundary values, silent
truncation at scale=256, and the off-by-one at scale=38.

Author: Pablo Carrera <pabloce@poscye.com>
Replace string-interpolated SQL with tiberius::Query parameterized
bindings in prepare_with/describe to eliminate Unicode homoglyph
injection edge case. Also log sp_describe_undeclared_parameters errors
instead of silently swallowing them.

Closes #10

Author: Pablo Carrera <pabloce@poscye.com>
…SQL escaping in test harness

- Map Datetime4 to SMALLDATETIME instead of DATETIMEOFFSET
- Validate rust_decimal scale <= 37 before u8 cast to prevent silent truncation
- Replace client-side .replace(']',"]]") escaping with parameterized QUOTENAME(@p1) + sp_executesql in testing/mod.rs

Author: Pablo Carrera <pabloce@poscye.com>
…arnings

- Add #[allow] annotations with SAFETY comments for all guarded integer
  casts that violated crate-level deny lints (cast_possible_truncation,
  cast_possible_wrap, cast_sign_loss)
- Parameterize advisory lock mode via @p2 instead of string interpolation
- Parameterize migrate table existence check via @p1 while preserving
  atomic single-statement DDL
- Change time_date_from_days to accept i64 with i64::from() at call
  sites to eliminate cast_sign_loss under --features time
- Remove unnecessary as u64 casts on Time::increments() calls
- Remove useless .into() on Error::Protocol(format!(...)) calls
- Extract build_columns_from_describe_rows helper to deduplicate
  prepare_with/describe column-building logic
- Fix minor issues: needless_borrow, needless_lifetimes, doc-test
  reborrows, todo!() -> error, uuid encode simplification, unused atoi
  dep, incorrect doc comment, redundant .into_iter()

Author: Pablo Carrera <pabloce@poscye.com>
… lossless casts

- Fix panic on multi-byte UTF-8 in catch-all error truncation by using
  is_char_boundary to retreat to a valid boundary
- Handle ColumnData::Xml(Some(...)) explicitly, converting to MssqlData::String
  via XmlData::into_string()
- Take ColumnData by value instead of by reference, eliminating allocations
  for String/Binary/Xml variants (into_owned + move instead of to_string/to_vec)
- Replace `as u64` with u64::from() for lossless widening casts in time encoding
- Extract repeated .time() calls into local bindings for clarity

Author: Pablo Carrera <pabloce@poscye.com>
Transform the feature overview into a self-contained guide covering
connection pooling, query patterns, FromRow/derive macros, error handling,
OUTPUT INSERTED, stored procedures, nested transactions, and pool callbacks.
Restructure sections to follow a developer's learning path, add recommended
feature flag sets, and correct begin_with_isolation usage and advisory lock
drop behavior.

Author: Pablo Carrera <pabloce@poscye.com>
Return Result from build_url(), document expect() invariants for UTC
offsets and epoch dates, safe UTF-8 truncation, lossless numeric casts,
parameterized sp_describe calls, and resolve all clippy deny violations.

Author: Pablo Carrera <pabloce@poscye.com>
…ge table

Relocate documentation to live alongside the driver code. Update the test
coverage table to fix an incorrect path and add missing test file entries.

Author: Pablo Carrera <pabloce@poscye.com>
Sync 33 upstream commits (3ec1422..6956cef) from launchbadge/sqlx into
the MSSQL fork.

Conflicts resolved:
- Cargo.toml: keep `sqlx-mssql` workspace entries; apply upstream's
  `default-features = false` on `sqlx-mysql` (for the new mysql-rsa
  optional feature).
- sqlx-macros-core/Cargo.toml: same pattern.
- Cargo.lock: accept upstream; pinned `home` to 0.5.11 to keep the
  workspace MSRV (1.86) viable.

Notable upstream commits that may affect MSSQL parity work:
- 45ba990 — feature: skip migrations (launchbadge#3846) — needs port to
  sqlx-mssql/src/migrate.rs (follow-up commit).
- 66533fa — feature: deterministic migration order (launchbadge#4136) — sqlx-core
  only; MSSQL inherits via trait.
- c0a3218 — breaking(any+mysql): correct text/blob -> AnyTypeInfo
  coercion (launchbadge#4255) — sqlx-mysql only; audit MSSQL's any.rs separately.

Pre-existing bug surfaced in `cargo check --workspace`: sqlx-mssql's
`types/time.rs` and `types/bigdecimal.rs` reference `MssqlData` variants
that don't exist when those features are enabled. Same failure on
pre-merge `main` — to be fixed in a follow-up, not introduced by this
sync.
Mirrors the upstream skip-migrations addition from launchbadge/sqlx
PR launchbadge#3846 (commit 45ba990). The new `skip` method records a migration
in the `_sqlx_migrations` table without executing its SQL body, marking
it as successfully applied. This is used by `sqlx migrate override skip`
in the CLI and is routed through the Any driver.

The TSQL INSERT mirrors `execute_migration`'s parameter binding
(`@p1, @p2, @p3`) and uses `1` for the `success` BIT column (MSSQL
doesn't have a TRUE literal). The `escape_table_name` helper provides
the same identifier-injection protection used by existing MSSQL
migration writes.

Author: Pablo Carrera <pabloce@poscye.com>
@pabl-o-ce pabl-o-ce closed this May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant