Skip to content

Conversation

@edwardneal
Copy link
Contributor

@edwardneal edwardneal commented Dec 28, 2025

Description

This PR emerges in part from discussion with @paulmedynski on #3858. We noticed that the json datatype wouldn't appear when running SqlConnection.GetSchema("DataTypes") against an Azure SQL instance. This is because of an underlying design choice in SqlMetaDataFactory: it only uses the SQL Server version number to control whether specific queries are used and specific records are returned. This isn't compatible with Azure SQL (which always returns a version of 12.x.) To fix this, we need SqlMetaDataFactory to be able to make decisions based upon the server capabilities, whether determined by the version, the presence of a FEATUREEXTACK token, or the contents of one of those tokens' values.

In this PR, we introduce a new type, ConnectionCapabilities. This class takes on the responsibility of parsing LOGINACK and FEATUREEXTACK tokens, then converting them into an object which SqlConnectionInternal, SqlMetaDataFactory and TdsParser can interrogate to check for feature availability. For good measure, I then plumb this object through to SqlMetaDataFactory. A subsequent PR can work out the best way to implement filtering.

LOGINACK parsing is handled by the new object's ProcessLoginAck method. This will look very different to the original TryProcessLoginAck method in TdsParser; the latter had a great deal of legacy baggage from needing to deal with SQL Server 2000 compatibility, which we no longer need to carry.

FEATUREEXTACK parsing is handled by ProcessFeatureExtAck. I've kept this fairly close to the original OnFeatureExtAck method in SqlConnectionInternal.

Other points of errata:

  • Moving the capability/version interrogation into the new type means that we no longer need to keep the original SqlLoginAck instance as a field on SqlConnectionInternal, which means that it never leaves the scope it was created in. I've turned it into a readonly ref struct.
  • There are a few places where SqlClient's processing of FEATUREEXTACK doesn't align with the TDS specification. I've not touched these to preserve the original behaviour, and have added a note where applicable.
  • 229a04d enables SqlClient to build under the Release configuration. This was necessary to get the benchmarking to work.
  • Benchmarking confirms that the CPU time, number of Gen0 GCs and memory usage remains identical (or very slightly better - slightly fewer Gen0 collections) between this branch and main.

This is a comparatively large PR, so I've tried to make it practical to move commit-by-commit.

Issues

Builds on a conversation in #3858.
Prerequisite to #3833.

Testing

I'm planning to make the coverage more explicit with unit tests for each relevant FEATUREEXTACK, but the existing test coverage should be fine.

We already have coverage (ConnectionTests.ConnectionTestDeniedVersion and ConnectionTestPermittedVersion) which specifically tests the LOGINACK component. Existing test functionality should prove the FEATUREEXTACK parsing - if there are issues parsing the relevant FEATUREEXTACK structures, the AlwaysEncrypted/vector/JSON/etc. tests will fail.

This class parses FEATUREEXTACK and LOGINACK streams, updating an object which specifies the capabilities of a connection.
This also means that we no longer need to hold the original SqlLoginAck record in memory.
This is adjacent cleanup which is best kept separate from the next step.
This includes the now-redundant checking (and associated exception) of the TDS version, and the assignment to _is20XX.
Remove the now-redundant _is20XX fields.
We only ever use two of these versions, and they're based on TDS versions rather than SQL Server versions.
Convert the Major/Minor/Increment bit-shifting to a constant value for clarity, and associate it with ConnectionCapabilities to eliminate duplication.
Also add explanatory comment to describe reason for big-endian vs. little-endian reads.
Move UTF8 feature detection handling to ConnectionCapabilities.
This was always equal to !Capability.DnsCaching.
This enables the if condition from OnFeatureExtAck to continue to apply to capability processing.

Also remove now-redundant comments, and clean up one reference to IsSQLDNSCachingSupported.
This is never persisted, and eliminates an allocation
One missing validation path.

Also switched conditions to use pattern matching and to better align with original method for easier comparison and better codegen.
@edwardneal edwardneal requested a review from a team as a code owner December 28, 2025 23:23
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