feat: modern SQL Server connectivity + TDS protocol + ergonomic APIs - #435
Closed
MattJackson wants to merge 36 commits into
Closed
feat: modern SQL Server connectivity + TDS protocol + ergonomic APIs#435MattJackson wants to merge 36 commits into
MattJackson wants to merge 36 commits into
Conversation
…tiberius-rs#412, tiberius-rs#340, tiberius-rs#414, tiberius-rs#224) Adapted from upstream tiberius-rs#413 (author @olback) onto the tiberius-rs#419 rustls-0.23 stack: - EncryptionLevel::Strict (TDS 8.0): TLS handshake before prelogin, ALPN 'tds/8.0' advertised on all three TLS backends. New tds80 feature (in default), with compile_error if enabled without a TLS backend. - Config::hostname_in_certificate(): validate the server cert against a specified name instead of the host (tiberius-rs#340). - Config::client_name() + default login hostname (workstation id) from the local machine name (tiberius-rs#414). - Connection-string parsing for HostNameInCertificate / WorkstationID and encrypt=strict. - Deps: async-native-tls 0.4->0.5 (request_alpns), libc (unix hostname). 142 lib tests pass; clippy --features=all -D warnings clean.
Fixes the docs.rs build (doc_status:false on 0.13.0-alpha.1). The crate gated #![feature(doc_cfg)] behind a 'docs' cargo feature, which failed to build. Switch all #[doc(cfg(...))] annotations to the standard #[cfg_attr(docsrs, ...)] pattern, set docs.rs to build with rustdoc-args=[--cfg docsrs], drop the unused 'docs' feature, and declare docsrs via [lints.rust] check-cfg so clippy -D warnings stays clean. Normal builds no longer require nightly.
Adapted from upstream tiberius-rs#331 (author @lpj145).
…rius-rs#314) Adapted from upstream tiberius-rs#314 (author @LazyDope).
…rs#298, tiberius-rs#307, tiberius-rs#373) Adapted from upstream tiberius-rs#298 (author @Geo-W).
…-rs#115) Adapted from upstream tiberius-rs#416 (author @MukundaKatta): new optional `serde` feature deriving Serialize/Deserialize for Row/Column/ColumnData/Numeric/ ColumnType and the time/xml types, with a round-trip test.
…s#366) Adapted from upstream tiberius-rs#366 (author @LonerDan), non-breaking (existing Config API retained).
Audited against MS-TDS: per-version support (7.1/7.3 full; 7.2/7.4/8.0 partial), feature matrix (messages/tokens/types/encryption/auth), and a prioritized gap list (SQL_VARIANT, named-proc RPC, COLINFO, FEDAUTHINFO...).
…iberius-rs#397, tiberius-rs#217, tiberius-rs#403) Adapted from upstream tiberius-rs#398 (author @etylermoss), reconciled with the already-integrated bulk_insert_columns (tiberius-rs#359): - Client::column_metadata(table, columns) returns owned Vec<MetaDataColumn> (names, types, size/precision/scale, nullability, identity); bulk insert now shares this path. - MetaDataColumn::col_name()/base() accessors; BaseMetaDataColumn:: is_identity()/is_nullable()/ty()/flags() (resolves tiberius-rs#403).
Add support for the SESSIONSTATE token (MS-TDS 2.2.7.22), used by the connection-resiliency / session-recovery feature. The server sends this token to communicate session state that a client can replay to transparently re-establish a broken connection. - Add TokenType::SessionState (0xE4) - Add TokenSessionState decoder (SeqNo, Status/fRecoverable, and a sequence of SessionStateData entries with the 0xFF long-length escape) - Dispatch and retain the token in the token stream (ReceivedToken::SessionState) - Add unit tests for the pure parse helper (short and long state lengths)
Add a TokenColInfo decoder (MS-TDS §2.2.7.4) and wire it into the token stream dispatch. Previously the COLINFO token type existed in token_type.rs but had no handler, so browse-mode responses that include COLINFO caused the stream to panic on an unimplemented token. The decoder parses each per-column entry (ColNum, TableNum, Status) plus the optional B_VARCHAR base-table column name emitted when the fDifferentName status bit is set, and exposes status accessors (expression/key/hidden/different-name). A new ReceivedToken::ColInfo variant carries the token; it is treated as informational and otherwise ignored, consistent with ORDER and INFO tokens. With every TokenType variant now handled, the unreachable catch-all dispatch arm is removed so the match is exhaustive. Includes a decode unit test covering entries with and without a differing column name.
Add the FEDAUTHINFO token (MS-TDS 2.2.7.12), sent by the server during a library-driven federated authentication (Azure AD / ADAL / MSAL) flow to tell the client how to acquire an access token. - Add TokenType::FedAuthInfo (0xEE). - Add TokenFedAuthInfo decoder parsing CountOfInfoIDs, the FedAuthInfoOpt option table, and the offset-addressed Unicode data region, exposing the STSURL and SPN information elements. - Dispatch the token in the token stream via ReceivedToken::FedAuthInfo, replacing the previous unimplemented panic. - Add unit tests for the pure parse helper (happy path, unknown info id, out-of-bounds offset).
The PRELOGIN encoder previously decoded INSTOPT/TRACEID/NONCEOPT but never emitted the instance-name or trace-id options it decoded. This adds: - INSTOPT emission: a null-terminated instance name (empty by default), letting the server validate the instance the connection landed on. - TRACEID emission: the client activity GUID + sequence, emitted only when an ActivityId is supplied, for server-side trace correlation. - PreloginMessage::validate_instance() to check the server's INSTOPT reply and surface a protocol error on rejection. - ActivityId::new() constructor for building a trace id. - Instance name plumbed from the connection config into the prelogin handshake, with the server response validated afterwards. Unit tests assert the encoded packet includes INSTOPT (empty and named), that TRACEID is present only when set and 20 bytes wide, and cover the instance-validation success/failure paths.
Implement the client-to-server Attention Signal packet (packet type 0x06, MS-TDS 2.2.1.6) and a query-cancellation API surface. - Add `PacketHeader::attention` producing an empty end-of-message attention packet. - Add `Connection::cancel_request`, which sends the attention packet and drains the token stream until the acknowledging DONE token with the DONE_ATTN status bit is received. - Add `TokenStream::flush_done_attention` to discard the tokens of the cancelled request up to that acknowledgement, and `TokenDone::is_attention`. - Expose `Client::cancel_query`, documenting how it integrates with futures cancellation / timeouts. - Add unit tests for the attention packet header and full-packet encoding.
The opentls (vendored OpenSSL) TLS backend cannot advertise the "tds/8.0" ALPN protocol required for TDS 8.0 strict-encryption negotiation ([MS-TDS] 2.2.6.5 PRELOGIN / TDS 8.0 addendum): opentls 0.2.x exposes no API to set the ALPN protocol list on its TlsConnector (no equivalent of openssl's SslConnectorBuilder::set_alpn_protos, and the wrapped SslConnector is private). Rather than silently degrading, the backend now: - exposes a documented TDS80_ALPN_PROTOCOL constant and a supports_alpn() helper (const fn returning false) explaining the limitation in rustdoc; - emits a clear WARN at TLS handshake time advising use of the native-tls or rustls backend when TDS 8.0 ALPN negotiation is required. Adds unit tests pinning the ALPN identifier and guarding the unsupported-backend invariant.
Add the TABNAME token (MS-TDS 2.2.7.21), which the server sends in browse mode (SELECT ... FOR BROWSE) to convey the table name(s) backing a result set. It is used together with the COLINFO token, whose entries reference these tables by index. - Add TokenType::TabName (0xA4). - Add TokenTabName decoder parsing the length-prefixed payload of one or more multi-part table names (NumParts + US_VARCHAR parts). - Wire a stream handler and ReceivedToken::TabName variant so browse-mode result streams no longer panic on the token. - Add unit tests for the pure payload parser and the async decoder.
Parse the UDT_INFO metadata (MS-TDS §2.2.5.5.4) for the 0xF0 UDT type in TypeInfo::decode: max byte size, database/schema/type names, and the assembly-qualified CLR type name. A new UdtInfo struct carries this metadata and TypeInfo gains a Udt variant. The column value path surfaces the raw UDT bytes (always transferred in the partially length-prefixed / PLP format) verbatim as ColumnData::Binary, without attempting CLR deserialization. ColumnType, the metadata Display impl, and the null-value mapping are all updated for the new variant. Adds unit tests for the metadata encode/decode round trip and for decoding both a populated and a NULL UDT value.
Add support for TDS Transaction Manager requests (MS-TDS 2.2.6.8), letting the driver begin, commit, roll back and create savepoints in a transaction directly through the protocol instead of issuing equivalent T-SQL batches. - Add `PacketHeader::transaction_manager` constructor for packet type 0x14 (TransactionManagerReq). - Add `TransactionManagerRequest` message with `Encode` covering the TM_BEGIN_XACT (5), TM_COMMIT_XACT (7), TM_ROLLBACK_XACT (8) and TM_SAVE_XACT (9) request kinds, including the ALL_HEADERS transaction descriptor block and B_VARCHAR name encoding. - Add public `IsolationLevel` and `TransactionManagerRequestType` enums. - Add `Client::begin_transaction`, `Client::begin_transaction_with_isolation`, `Client::commit_transaction`, `Client::rollback_transaction` and `Client::save_transaction`. The server's BeginTransaction/ CommitTransaction/RollbackTransaction ENVCHANGE tokens are already applied to the connection context, so the active transaction descriptor is tracked automatically. - Re-export `IsolationLevel` from the crate root. - Add unit tests for the request encoding.
Reading a `sql_variant` column previously hit a `todo!()` in `token_col_metadata` and panicked. This adds a `VarLenType::SSVariant` TypeInfo parse path and a decoder for the SQL_VARIANT value, per MS-TDS 2.2.5.4.4 (metadata) and 2.2.5.5.3 (value). The value carries a base-type byte, a property-bytes count, the type-specific property metadata (collation, precision/scale, scale) and the raw value. The decoder maps the stored intrinsic onto the matching `ColumnData` variant (e.g. `int` -> `I32`, `nvarchar` -> `String`). Supported base types: bit, tinyint/smallint/int/bigint, real/float, money/smallmoney, datetime/smalldatetime, uniqueidentifier, numeric/decimal, char/varchar/nchar/nvarchar, binary/varbinary, and (under `tds73`) date/time/datetime2/datetimeoffset. A NULL sql_variant (zero total length) is surfaced as a generic null. Adds unit tests decoding representative payloads (int, bigint, bit, nvarchar, varchar, binary, guid, numeric, date, null) built from hand-computed spec bytes.
Add support for the ALTMETADATA (0x88) and ALTROW (0xD3) token streams that SQL Server emits for COMPUTE / COMPUTE BY result sets, per MS-TDS sections 2.2.7.1 and 2.2.7.2. Previously any query containing a COMPUTE clause failed with "invalid token type 88" because these tokens were unknown to the parser. - Add AltMetaData and AltRow variants to TokenType. - Add TokenAltMetaData / AltMetaDataColumn with a decoder that reads the aggregate operator, operand, per-column metadata (reusing BaseMetaDataColumn) and the computed column name. - Add TokenAltRow with a decoder that parses the computed values using the ALTMETADATA identified by the row's compute id. - Cache ALTMETADATA in the connection Context keyed by compute id so a following ALTROW can be resolved. - Wire both tokens into the token stream (NewAltResultset / AltRow received tokens). The user-facing QueryStream ignores them, so normal result sets are unaffected and COMPUTE BY queries no longer error. - Add decode unit tests for both tokens. Remaining work: surface ALTROW values to consumers as a dedicated result-set variant (currently decoded and skipped).
Add examples/named-pipes.rs demonstrating how to connect to SQL Server over a Windows named pipe by wrapping the pipe stream with the tokio-util compatibility layer and handing it to Client::connect. Register the example in Cargo.toml. The named-pipe transport is Windows-only, so the example is cfg-gated and reports unsupported on other platforms. Documents tiberius-rs#131 and tiberius-rs#53.
Integrate upstream PR tiberius-rs#378 (resolves tiberius-rs#337). Add a `multi_subnet_failover` option to `Config` with a setter/getter and ADO/JDBC connection-string parsing of the `MultiSubnetFailover` keyword (via the shared `ConfigString` trait, so it works for both string formats). When enabled, the SQL Browser `connect_named` paths (tokio, async-std, smol) attempt connections to every resolved address in parallel using `FuturesUnordered` and return the first stream to succeed; otherwise they fall back to the existing sequential behaviour. The per-address connect logic is factored into a `connect_addr` helper, and the first observed error is now preserved instead of being masked by a generic "not found". Adds unit tests for keyword parsing and end-to-end `from_ado_string` propagation.
Bump the remaining dependencies and fix the resulting API breakage: - thiserror 1 -> 2 - bigdecimal 0.3 -> 0.4 - pretty-hex 0.3 -> 0.4 - async-native-tls 0.5 -> 0.6 (drop removed runtime-async-std feature) - asynchronous-codec 0.6 -> 0.7 (Encoder::Item is now a GAT) - async-io 1.8 -> 2, async-net 1.7 -> 2, futures-lite 1.12 -> 2 - libgssapi 0.8.1 -> 0.11 (OidSet::new no longer returns Result; add/Name::new take Oid by value) - winauth 0.0.4 -> 0.0.5 - dev: azure_identity 0.20 -> 1.0 (+ azure_core 1; ClientSecretCredential replaces the removed client_credentials_flow), reqwest 0.12 -> 0.13, indoc 1 -> 2, url 2.2 -> 2.5 Drop the now-stale RUSTSEC-2024-0384 (instant) and RUSTSEC-2026-0174 (http-types) advisory ignores from deny.toml since the bumps remove those crates from the graph.
Add an optional `sspi-rs` feature that enables `AuthMethod::Windows` (and `AuthMethod::windows()`) on Unix platforms, implementing the NTLM handshake with the pure-Rust `sspi` crate. This provides Windows-style authentication without requiring a Kerberos/GSSAPI setup, closing the gap for Linux and macOS clients (tiberius-rs#407, tiberius-rs#276, tiberius-rs#97). - Add `sspi` (unix target) dependency and `sspi-rs` feature; include it in the `all` feature set. - Gate `WindowsAuth`, the `AuthMethod::Windows` variant and the `windows()` constructor on `all(unix, feature = "sspi-rs")` in addition to the existing Windows `winauth` path. - Implement the two-leg NTLM negotiate/authenticate exchange in `Connection::login` for the Unix `sspi-rs` path, reusing the existing SSPI token flushing and `integrated_security` login plumbing. - Extend the connection-string parser so `IntegratedSecurity=SSPI` selects NTLM when a username/password is supplied and falls back to Kerberos (`Integrated`) only when `integrated-auth-gssapi` is enabled and no credentials are given. - Add an `Error::SspiRs` variant and `From<sspi::Error>` conversion. The existing Windows `winauth` and Unix `integrated-auth-gssapi` paths are left intact; the gssapi connection-string arm is only disabled when `sspi-rs` is active on Unix to avoid overlapping match arms.
…eters Implement calling a stored procedure by name, resolving the `todo!()` that previously blocked named-procedure RPC requests. - Encode `RpcProcIdValue::Name` as a US_VARCHAR proc name in the RPC request, alongside the existing by-id path used by execute/query. - Introduce `RpcValue` (Scalar/Table) so RPC params can carry either a scalar `ColumnData` or a table-valued parameter, and thread it through the existing execute/query call sites. - Add `TypeInfoTvp` to encode TVP type info and rows (MS-TDS 2.2.5.5.5), rewriting fixed-length column types to their nullable var-len variants. - Add a `Command` public API (`bind_param`, `bind_out_param`, `bind_table`, `bind_table_with_dbtype`, `exec`) plus `CommandStream`, `CommandItem`, `CommandResult` and `CommandReturnValue` for reading OUT parameter values, return codes and result sets. - Add the `tvp-macro` crate providing `#[derive(TableValueRow)]`. - Encode nullable scalar values correctly when no TypeInfo is supplied. Unit tests cover named-proc request encoding, TVP type-info encoding and the derive macro output. Integration tests requiring a live SQL Server are added under tests/command.rs. Resolves tiberius-rs#275.
Add support for presenting a client certificate during the TLS handshake to authenticate the client to the server (mutual TLS), required for TDS 8.0 strict connections using ENCRYPT_CLIENT_CERT and usable with the classic TLS handshake when the server requests a client certificate. - Config/ConfigBuilder gain `client_certificate(cert, key)` for PEM/DER certificate + private-key files, and `client_certificate_pkcs12(path, password)` for a PKCS#12/PFX bundle (native-tls and vendored-openssl). The PKCS#12 password is held in a zeroizing buffer and redacted in Debug output. All new public API is feature-gated with docsrs doc(cfg). - rustls: wired via `with_client_auth_cert` on the ConfigBuilder path; loads PEM (chain) or DER certificates and PEM/DER private keys. PKCS#12 is rejected with a clear error. - native-tls: wired via `Identity` (PEM PKCS#8 files or PKCS#12 bundle). - vendored-openssl (opentls): wired via `Identity::from_pkcs12`; separate PEM/DER files are unsupported by the backend and rejected with a clear error documenting the limitation. - Unit tests for the config plumbing (cert/key and PKCS#12 sources, builder wiring, password redaction).
Config::builder() returns a ConfigBuilder, but the type was not nameable from outside the crate. Export it so downstream code can hold and pass a builder value.
(cherry picked from commit 1f7bddf1c157b24b14ee95546847d080694ee672)
SQL Server sets reserved/ODBC bits in the 16-bit COLMETADATA Flags field (MS-TDS 2.2.7.4); strict BitFlags::from_bits rejected the whole token with 'invalid flags' (broke tests/custom-cert). Truncate to the modelled flags. Also: declare MSRV 1.88 (rust-version), add an MSRV CI job and an advisory semver-checks job on PRs, and canonicalize tvp-macro's license expression.
BREAKING: removes async-std as a supported runtime. async-std is discontinued (RUSTSEC-2025-0052); this removes the sql-browser-async-std feature, its SQL Browser impl, the async-std example/named-instance test, and the async-std dev-dependency, eliminating async-std from the dependency graph entirely (clears the RUSTSEC-2025-0052 ignore in deny.toml). - Modernize the runtimes-macro test helper to syn 2 (drop syn 1 + darling) and retarget #[test_on_runtimes] to generate tokio + smol variants (was tokio + async_std). All 111 call sites are unchanged; add smol as a dev-dependency. - Rewrite the async-std crate-doc and SQL Browser doctests to tokio. - Update README/CONTRIBUTING/CI feature lists to tokio + smol. Downstreams on sql-browser-async-std should switch to sql-browser-tokio or sql-browser-smol.
The bulk_insert and bulk_insert_columns doctests both created global temp
table ##bulk_test; run in parallel they raced and one hit error 2714
('object already exists'). Give each a distinct name so they never collide.
This was referenced Sep 1, 2026
Contributor
Author
|
Superseded by #442. Re-opened as a native GitHub stacked PR (stack #445) per @aqrln's request in #440 — the fork origin of these PRs made a native stack impossible (GitHub can't add fork-head PRs to a stack), so the branches now live in |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The feature layer of the sync — modern connectivity, deeper TDS protocol coverage, and ergonomic APIs.
refactor!: drop async-std(dual-runtime tests retargeted to tokio + smol). Warrants a minor/major version bump per your scheme, not a patch.hostname_in_certificate+client_name; MultiSubnetFailover; SSPI/NTLM on Unix via sspi-rs; client-certificate (mTLS); named-pipes example.ConfigBuilder;column_metadata()+ identity accessors; Transaction Manager (begin/commit/rollback); named-procedure RPC with OUT params + table-valued parameters via a newtiberius-macrosderive crate;TokenRowaccessor; moreIntoSql/FromSqlconversions; DateTime2→datetime coercion under tds73.Granular per-feature commit history preserved.
Supersedes #132, #298, #314, #328, #331, #357, #366, #378, #398, #408, #413, #416.
Sequential series — merge after #432, #433, #434. Based on
main; diff reduces to its own 36 commits as the earlier PRs land.Reviewer note: please rebase-merge or merge-commit, not squash.