Add vector(float16) support - #4501
Conversation
A vector column's base type and number of dimensions were already available from the column schema, but only as a numeric scale and a column size which the caller had to decode. They are now surfaced under their own names, so that applications inspecting result set metadata do not have to know that encoding. Also registers the vector type in the DataTypes schema collection, where it was missing. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
SQL Server transports vector(N, float16) elements as raw binary16 values. System.Half is only available on .NET, so the conversion is implemented manually for .NET Framework. The manual implementation is compiled for every target framework rather than only for .NET Framework, so that it can be validated exhaustively against System.Half on .NET while remaining the code path .NET Framework actually uses. It is verified against every binary16 bit pattern, a strided sweep of the single precision range, and the rounding, subnormal, overflow and underflow boundaries. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
Advertises version 2 of the VECTORSUPPORT feature extension, so that a vector(N, float16) column is exchanged in its native binary form rather than as a varchar(max) JSON string. On .NET such a column is surfaced as SqlVector<Half>. .NET Framework has no System.Half, so it is reported as a string there, matching how it is already presented when the server does not negotiate float16 support. Callers on either framework can explicitly request a strongly typed value via GetSqlVector<float>, which widens the elements without loss. SqlVector<T> continues to derive the base type written to the wire from T alone. Conversion between base types is left to the server, which performs it for parameters. Bulk copy is the exception: it declares the destination's base type in the INSERT BULK statement, so a payload using a different base type is rejected as a column length error rather than converted, and is rewritten by the driver first. That conversion runs after coercion, because the payload coercion produces uses the source value's own base type: a JSON string always yields float32, which is how a float16 column reads back where System.Half is unavailable. SqlVector<T>.ToString() now returns the vector's values as a JSON array rather than the type name. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
Describes the base types a vector column can have, how they map to SqlVector<T>, and how a float16 column is read and written on .NET Framework, where System.Half does not exist. Also documents the vector feature extension versions and the column metadata properties. Adds a sample covering both frameworks, reading a float16 column as an exact, widened or JSON value, inspecting a column's base type and dimensions, and converting between base types. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
There was a problem hiding this comment.
Pull request overview
Adds end-to-end support for SQL Server vector(N, float16) by negotiating VECTORSUPPORT feature extension version 2, introducing an IEEE-754 binary16 codec, and wiring float16 handling through SqlVector<T> read/write paths (including bulk copy), with accompanying docs and tests.
Changes:
- Negotiate vector feature extension v2 and track negotiated vector capability version (float32/float16) on the connection.
- Add float16 vector support across
SqlVector<T>,SqlDataReader,SqlBuffer,SqlParameter,SqlCommand, andSqlBulkCopy, including payload conversion for bulk copy. - Add unit/manual tests plus docs/snippets/sample updates; expose vector base type + dimensions via
DbColumnindexer and registervectorin theDataTypesschema collection.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs | Updates simulated negotiation tests for vector feature extension v2. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlVectorTest.cs | Adds float16 construction/rendering tests and payload conversion tests. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/Float16ConverterTest.cs | New unit tests validating binary16 codec (incl. exhaustive bit-pattern validation on .NET). |
| src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorFloat16BehaviourTests.cs | Manual tests for float16-specific behaviors (read/write/cross-base-type/bulk copy). |
| src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorColumnMetadataTests.cs | Manual tests for vector column metadata + schema collection registration. |
| src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat16Tests.cs | Manual typed tests for SqlVector<Half> on .NET. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs | Implements float16 support in SqlVector<T>, adds JSON ToString(), payload widening/conversion helpers. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs | Adds vector version constants and sets max supported vector version to float16 (v2). |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs | Handles float16 vector return/coercion paths (Half on .NET, widening on netfx). |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs | Registers vector in GetSchema("DataTypes"). |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs | Adds float16 vector element type and element-size mapping; meta type inference includes SqlVector<Half> on .NET. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs | Exposes VectorBaseType/VectorDimensions via DbColumn indexer. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs | Adds float16 field-type mapping and broadens GetSqlVector<T> to allow Half on .NET. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs | Emits (N, float16) parameter declaration when needed (keeps float32 declaration unchanged). |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs | Emits float16 vector type in INSERT BULK declaration and converts payload base type to match destination. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs | Centralizes float16 vector rendering/value-shaping across frameworks. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs | Records negotiated vector feature version on FEATUREEXTACK. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/ConnectionCapabilities.cs | Replaces bool flag with VectorVersion and derived float32/float16 capability properties. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/Float16Converter.cs | New internal binary16<->binary32 conversion implementation. |
| src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlTypes.cs | Updates reference surface to include SqlVector<T>.ToString() override. |
| doc/snippets/Microsoft.Data.SqlTypes/SqlVector.xml | Documents float16 support, size constraints, and ToString() JSON rendering. |
| doc/samples/SqlVectorFloat16Example.cs | New sample demonstrating float16 vectors (insert/read/metadata). |
| .github/instructions/features.instructions.md | Updates repo feature reference docs for float16 vector base type and negotiation versions. |
Suppressed comments (1)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs:420
ConvertPayloadElementTypedoesn’t validate the vector header magic/version bytes before using the length and element type fields. This can cause non-vector payloads to be converted (or to fail later with less appropriate exceptions). ValidateVecHeaderMagicNo/VecVersionNoup front, consistent withGetCountsOrThrow.
if (tdsBytes.Length < TdsEnums.VECTOR_HEADER_SIZE)
{
throw ADP.InvalidVectorHeader();
}
Bulk copy read a vector column through the representation the reader surfaces, which is a JSON string on frameworks without System.Half. That round trip is both larger than the payload it encodes and unable to carry a negative zero, because System.Text.Json on .NET Framework serialises one as zero and parses a negative zero literal back as positive zero. Reading the payload directly avoids both. It is chosen once per column, when the source and destination are both vector columns, alongside the existing decimal and streaming decisions. Any difference in base type between the two is still resolved when the value is converted. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs:1755
SqlTypes.SqlVector<float>doesn’t resolve to any namespace/type in this file (there’s nousing SqlTypes = ...and noSqlTypesnamespace). This should be fully qualified toMicrosoft.Data.SqlTypes.SqlVector<float>(or add an alias) to avoid a compile error.
// The payload is converted directly rather than through a strongly typed vector,
// so that .NET Framework, which has no System.Half, can also write to float16
// destinations.
return SqlTypes.SqlVector<float>.ConvertPayloadElementType(payload, destinationElementType);
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs:154
FromTdsPayloadreads header fields (element type/length) without validating the vector magic/version bytes. This makes the widening path accept malformed payloads thatGetCountsOrThrowwould reject.
if (tdsBytes.Length < TdsEnums.VECTOR_HEADER_SIZE)
{
throw ADP.InvalidVectorHeader();
}
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs:420
ConvertPayloadElementTypeshould validate the vector header magic/version before interpreting element type and length; otherwise malformed byte[] values can be converted and sent on the wire rather than failing fast with InvalidVectorHeader.
if (tdsBytes.Length < TdsEnums.VECTOR_HEADER_SIZE)
{
throw ADP.InvalidVectorHeader();
}
doc/samples/SqlVectorFloat16Example.cs:146
- These interpolated strings won’t compile because the expression uses double quotes (e.g., column["VectorBaseType"]) inside a double-quoted string literal. Escape the quotes (or assign to a local variable) before interpolating.
Console.WriteLine($"\nColumn base type: {column["VectorBaseType"]}");
Console.WriteLine($"Column dimensions: {column["VectorDimensions"]}");
The existing suite covers nulls where the source and destination share a base type, but not where they differ, which is the path that converts the payload. Verified that nulls survive in every combination, interleaved with non-null rows so that a row's nullness cannot be satisfied by position alone. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.
Suppressed comments (2)
doc/samples/SqlVectorFloat16Example.cs:146
- These interpolated strings won’t compile because the expression contains a string literal with double quotes (e.g., column["VectorBaseType"]) which terminates the outer interpolated string. Assign the indexer results to variables (or constants) first, then interpolate those variables.
Console.WriteLine($"\nColumn base type: {column["VectorBaseType"]}");
Console.WriteLine($"Column dimensions: {column["VectorDimensions"]}");
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs:2408
- This SqlVector special-case is redundant/unreachable because SqlVector implements ISqlVector (so it will already be handled by the earlier
value is ISqlVectorbranch). Keeping the extra branch increases maintenance burden and risks diverging behavior.
else if (currentType == typeof(SqlVector<Half>))
{
value = ((ISqlVector)value).VectorPayload;
}
#endif
saurabh500
left a comment
There was a problem hiding this comment.
Summary
This adds vector(N, float16) by advertising VECTORSUPPORT v2, introducing a hand-written binary16 codec, teaching SqlVector<T> about System.Half, and switching vector→vector bulk copy to a raw-payload transfer. The engineering is careful, and I want to call out specifically that the endianness and element-size arithmetic is correct throughout — I went looking for a missed (ColumnSize - 8) / 4 and there isn't one. The commit split is clean and the rationale in the description is unusually good.
I have one blocking correctness issue, plus a set of suggestions. Inline comments carry the detail and suggested diffs; this is the map.
Blocking
SqlBuffer.GetSqlVector<T>()succeeds or throws depending on the row's nullness. TheIsNullbranch builds a vector for anyTwithout consulting the column's base type, while the non-null branch validates.GetSqlVector<Half>()over afloat32column therefore returnsNullfor NULL rows and throwsNotSupportedExceptionfor non-NULL rows in the same result set. Data-dependent rather than schema-dependent, so it is hard to find in testing and impossible to guard against in caller code.
Suggestions
- The codec is not bit-exact against
System.Halffor NaN, contrary to the description, and the tests are written to step around exactly that case (continue/IsNaN-only).float.NaNcarries the sign bit, so widening flips the sign of every NaN relative toSystem.Half; narrowing canonicalises payloads that(Half)floatpreserves. Either matchSystem.Halfor pin the canonical form with explicit assertions and drop the "bit-exact" claim. - Bulk copy's vector case uses
metadata.scalerather than thescalelocal that the surrounding code establishes for encrypted columns. - The widening path skips the magic-number and version validation that the matching path gets via
GetCountsOrThrow. Capabilities.Float16VectorTypeis never read anywhere insrc/, so aSqlVector<Half>on a v1-negotiated connection fails server-side rather than client-side. (Float32VectorTypewas already dead inmain; this adds a second.)- The negotiation theory's
0x3case doesn't test what its comment claims — the simulated server caps the ack itself, so the client's own ceiling check atSqlConnectionInternal.cs:1660stays untested. - Docs say narrowing "fails for values outside its range"; the code saturates to ±Infinity and leaves it to the server.
MetaDatais dereferenced without a null check inCreateSourceColumnMetadata.
Things I checked and found correct
Worth recording, since they're the parts most likely to be wrong in a change like this:
- Subnormal widening, signed zero, overflow to infinity, the flush-to-zero boundary (2⁻²⁵ ties to even → 0, 2⁻²⁶ flushes), binary32 subnormal inputs, and the rounding carry into the exponent are all correct. Compiling
Manual*on every TFM so the netfx path is under test on .NET too is a good arrangement. - Bulk copy null handling is correct as-is —
GetValueFromSourceRowreturnsDBNull.ValuewithisNull = trueandConvertValuereturns beforeConvertVectorToBaseType. Notea5de733c3is test-only; it documents behaviour that already worked rather than fixing anything. - The raw-payload path can't be taken by
DataTable,DataRow[], or non-SqlClientDbDataReadersources (they keepValueMethod.GetValue), and reordering column mappings are safe becausesourceOrdinalis the mapped ordinal. - No unacknowledged breaking changes beyond the three listed:
GetDataTypeNameis unchanged,GetFieldType/GetProviderSpecificFieldTypeboth route through the single newGetVectorFieldTypeso they stay consistent,SqlMetaDataFactory.DataTypesonly adds a row (gated onMinimumVersionKey), thefloat32declaration is deliberately unchanged, andSqlDbColumn's new indexer falls through tobase[property]. - No shared mutable state:
Float16Converteris stateless andSqlVector<T>is areadonly structwith no static caches.
On testing
Taking as given that CI has no float16-capable server and no Azure SQL DB connectivity, so the manual suite is the only gate that will ever run — I looked at whether it is complete enough for a lab run rather than whether CI covers it. It is substantial: 11 behaviour tests plus the inherited NativeVectorTestsBase matrix, and the sample data is well chosen (Half.MaxValue, Half.Epsilon, -0.0f, exactly-representable eighths). Gaps I'd close:
- .NET Framework gets none of the
NativeVectorTestsBasematrix —NativeVectorFloat16Tests.csis entirely#if NET. That is where the hand-rolledManual*codec is the production path. - No async coverage for the new representation on any framework, and none at all for float16 on netfx.
DataTestUtility.CheckVectorFloat16Supportedfails open through the code under test. It reads the probe vector withGetString+JsonSerializer.Deserializeand catchesJsonException→false. A driver regression that produces malformed JSON silently skips the whole float16 suite green. Since the manual run is the only gate, that is the wrong failure mode. (Outside this diff, so no inline comment — but worth fixing alongside. It also leavesPREVIEW_FEATURES = ONon the shared test database as a side effect.)- Two range tests assert only that some
SqlExceptionwas thrown; they'd pass on an unrelated failure, and they can't distinguish "client rejects" from "client saturates and server rejects" — which is exactly the ambiguity in the doc wording above. - Nothing covers the blocking issue: reading a float32 column as
SqlVector<Half>, for a NULL and a non-NULL row. - Bulk copy with a dimension-count mismatch between source and destination is uncovered. Mitigating:
float32→float32now routes through the new raw-payload path too and is covered by the existingNativeVectorFloat32Tests, which runs against any vector-capable server — so regression risk to shipped functionality is covered.
Minor
SqlVector<T>.ToString()changing for existingSqlVector<float>callers is justified and correctly surfaced in the ref assembly and docs, but it is unrelated to float16 — it wants its own release-note entry as a behavioural break, not just an API-list line. Related:GetString()usesJsonSerializer.Serialize, which throws onNaN/Infinityby default, and on .NET Framework that is now the defaultGetValue()path.ConvertPayloadElementTypeisinternal staticonSqlVector<T>but never usesT, so callers writeSqlVector<float>.ConvertPayloadElementType(...), which reads as though it returns a float32 result.- On .NET Framework the reader surfaces a float16 column as
stringwhile an output parameter surfaces it asSqlVector<float>. Self-consistent, but worth documenting. - Preprocessor directives in the new code are indented to the surrounding block; the dominant style in these files is column 0.
ConnectionCapabilities.cs:179says vectors were "introduced in SQL Server 2022" — pre-existing, but the newFloat16VectorTypedoc sits right beside it.- Worth confirming the
doc/samplesbuild resolves the locally-packed driver:SqlVectorFloat16Example.csreferencesSqlVector<Half>andGetSqlVector<Half>, which exist in no released package.
Review assisted by GitHub Copilot; findings verified against the code at a5de733c3.
|
@apoorvdeshmukh and @cheenamalhotra, I think we will need to call this API a known limitation for preventing backward migration from .Net Runtime to NetFx. I believe this is a known reasonable compromise. |
|
BTW, I saw changes to ref assembly. Should I expect two copies of changes, one for netcore and another for NetFx? I am curious about how the APIs will show up in contract assemblies targeting 2 different frameworks. |
Correctness - Reject a narrowing read consistently for null and populated rows. The element type is now checked before the null check in GetSqlVector<T>, so reading a float32 column as SqlVector<Half> fails for every row rather than succeeding for the null ones. - Validate the vector header's magic number and version on the widening and payload conversion paths, which previously checked only the length. - Quieten a signalling NaN when widening to single precision, and preserve a NaN's sign and payload in both directions, so the hand written codec and System.Half agree on all 65,536 bit patterns. - Read the redirected scale when converting a bulk copy value, so an encrypted column uses its base type rather than the wrapping metadata's. - Guard against a null MetaData when deciding whether a bulk copy source can supply a raw vector payload. Behaviour - Report a value which cannot be narrowed to float16 during a bulk copy as an OverflowException, rather than saturating it to an infinity and letting the server reject the result as a malformed vector. - Remove the unused Float32VectorType and Float16VectorType capability properties. The negotiated version is still recorded in VectorVersion. A client side guard was considered in their place, but the server already reports an unrecognised base type clearly, so the guard would only have replaced a good error with a worse one, and would have made float16 fail differently from float32 for the same cause. - Return the JSON rendering of a float16 vector as a SqlString from the provider specific accessors, so that every provider specific value remains a type from System.Data.SqlTypes. GetValue continues to return a string. Tests - Run the whole native vector matrix against a float16 column through the single precision representation, which covers .NET Framework, where the hand written codec is the production path rather than a test double. - Assert NaN bitwise rather than skipping it, which is what allowed the codec divergence above to go unnoticed. - Cover reading a float32 column as a narrower vector, for null and populated rows, synchronously and asynchronously. - Exercise the client's own feature extension version ceiling, by letting the simulated server acknowledge a version regardless of what the client requested. The existing case only proved the harness capped the version. - Assert the server's error number for an out of range value rather than accepting any SqlException. - Show the column metadata driving a read for a caller which does not know the schema in advance. - Remove the SqlVector<T>.ToString() override added earlier in this branch. It changed the rendering of the already shipped SqlVector<float> as well, and the reader already exposes the JSON form through GetString and GetFieldValue<string>. The internal GetString is unchanged. - Move Float16Converter into the Microsoft.Data.Common namespace, matching the folder it lives in and its neighbours there. Docs - State that a bulk copy reports an out of range narrowing itself, and correct the SQL Server version for the float32 base type. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
There was a problem hiding this comment.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Note
This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.
Two tests encoded behaviour of the SQL Server 2025 build they were written against, and failed on every Azure SQL leg. Rows in the DataTypes schema collection are filtered by the version the server reports, and the vector row is declared from 17.00 onwards. Azure SQL reports 12.00 whatever it supports, so the row is filtered out there even though the type is available. The json type, declared the same way, has the same gap. Skip the test where the version is not meaningful. An out of range narrowing to float16 is reported as error 42284 by SQL Server 2025 and 42241 by Azure SQL. Both describe the same overflow, so accept either rather than the one number that happened to be observed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 39 out of 40 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs: Generated file
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs:1232
- The comment says a base-type mismatch between source and destination is resolved by conversion, but the VectorPayload path preserves the raw payload (and therefore its base type). Mismatches are expected to be reported by the server, and only textual sources are rewritten client-side.
This issue also appears on line 1459 of the same file.
// Transfer the vector as its raw payload, so that no value is
// lost to an intermediate representation and no larger textual
// form is sent. Any difference in base type between the source
// and the destination is resolved when the value is converted.
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs:1463
- The XML doc comment describes vector bulk-copy values being transferred as text and converted by the server, and implies the destination column is declared as varchar(max). The implementation actually coerces JSON strings to a vector payload client-side and sends binary; the server does not parse JSON within the bulk-copy stream.
/// <summary>
/// Whether a vector destination column is supplied from a textual source, in which
/// case the value is transferred as text and converted by the server.
/// </summary>
/// <remarks>
The vector row in the DataTypes schema collection was declared from server version 17.00 onwards, and rows in that collection are filtered by the version the server reports. Azure SQL reports 12.00 whatever it supports, so the row was filtered out there even though the type was available, and the test covering it failed on every Azure leg. The metadata factory is already given the connection's capabilities, which carry the version negotiated through the VECTORSUPPORT feature extension. That is what decides whether vector columns are read as vectors, so report the type from it rather than inferring it from a version string. A connection which opted out with Vector Type Support=off reads vector columns as varchar(max), and so no longer reports a vector type. The json type is declared the same way and has the same gap on Azure SQL, but its behaviour has shipped, so it is left alone here. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
Every keyword written with spaces has a synonym written without them: Trust Server Certificate is also trustservercertificate, Host Name In Certificate is also hostnameincertificate, and so on. Vector Type Support was registered without one, so VectorTypeSupport was rejected as an unsupported keyword. That is also the spelling the JDBC driver uses, so it is the form an application ported from it would be written with. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 42 out of 43 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs: Generated file
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorColumnMetadataTests.cs:158
- Inside the row loop,
Assert.Contains(baseType, new[] { "float16", "float32" })is redundant/confusing becausebaseTypeis already asserted to be "float16" above and never changes per row. Keeping this check suggests the query returns mixed base types, but it doesn’t.
The comment on IsTextSourcedVectorColumn described an earlier design in which the column was declared as a varchar(max) and the server parsed the JSON array. The server rejects that declaration for a vector column, so the client parses the text and rewrites the payload to the destination's base type instead. The call site already says so; only this comment was left behind. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e17ed782-c576-4cb7-9b4b-7ad286d7a7d0
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4501 +/- ##
==========================================
- Coverage 64.60% 63.66% -0.94%
==========================================
Files 288 287 -1
Lines 44046 68404 +24358
==========================================
+ Hits 28455 43551 +15096
- Misses 15591 24853 +9262
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Adds support for the
float16base type of the SQL Servervectordata type, on both .NET and .NET Framework.Behaviour
float16is exchanged in its binary form only when a connection opts in, through the newVector Type Supportkeyword (off|v1|v2, defaultv1). This mirrorsvectorTypeSupportin the JDBC driver, including its default. Atv1afloat16column is returned as avarchar(max)containing a JSON array, exactly as today, so upgrading the driver changes nothing until an application asks forv2.At
v2:GetValue/GetFieldTypeSqlVector<Half>string(JSON array)GetSqlVector<Half>GetSqlVector<float>— widening, which is exactSqlVector<Half>,SqlVector<float>, or a JSON stringSqlVector<float>or a JSON stringSystem.Halfdoes not exist on .NET Framework, so afloat16column is surfaced there as its JSON rendering rather than by substituting a different base type. A caller that wants a strongly typed value asks forSqlVector<float>explicitly.Column metadata
A vector column now reports its base type and dimension count through the existing
DbColumnindexer, with no new API:This is the only way to distinguish the two base types when a
float16column is surfaced as a JSON string, becauseGetFieldTypereportsstringfor such a column just as it does for avarcharone.Bulk copy
The
INSERT BULKstatement states the destination column's base type, and the server then requires a binary payload of exactly that width and performs no conversion within the data stream. Measured at every negotiated version: a text declaration for a vector column is refused withInvalid column type from bcp client, and text sent under a vector declaration is refused with a length mismatch. Only a client which never negotiates the feature extension may send text.So a textual source is parsed into the destination's base type by the driver, and a payload read from another vector column keeps its own base type — copying between columns of different base types is reported by the server rather than silently narrowed. Both match the JDBC driver.
Public API
One addition: the
SqlVectorTypeSupportenum andSqlConnectionStringBuilder.VectorTypeSupport.SqlVector<T>is unchanged.Tests
float16added to the existing generic native vector suite, run over av2connectionSqlVector<float>on every framework, which is the .NET Framework representation and where the hand written binary16 codec is the production pathSystem.Halfacross all 65,536 binary16 and all 4,294,967,296 binary32 patternsChecklist
Notes for reviewers: the second commit reworks bulk copy and negotiation following a design review, so it is worth reading over the first. One review thread is left open pending that discussion.