Skip to content

Return correct type when invoking GetFieldType and GetProviderSpecificFieldType for vector float32 column#4105

Merged
apoorvdeshmukh merged 5 commits intomainfrom
dev/ad/4104
Apr 7, 2026
Merged

Return correct type when invoking GetFieldType and GetProviderSpecificFieldType for vector float32 column#4105
apoorvdeshmukh merged 5 commits intomainfrom
dev/ad/4104

Conversation

@apoorvdeshmukh
Copy link
Copy Markdown
Contributor

Description

Fixes #4104.
GetFieldType returned type from MetaType.ClassType
The fix follows similar pattern as GetValue to determine correct type for Vector metatype.
The fix has been extended to GetProviderSpecificFieldType which had similar issue.

Issues

#4104

Testing

Extended NativeVectorFloat32Tests with test coverage for above APIs.

Guidelines

Please review the contribution guidelines before submitting a pull request:

@apoorvdeshmukh apoorvdeshmukh requested a review from a team as a code owner March 31, 2026 09:55
Copilot AI review requested due to automatic review settings March 31, 2026 09:55
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board Mar 31, 2026
@apoorvdeshmukh apoorvdeshmukh added this to the 7.0.1 milestone Mar 31, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes SqlDataReader.GetFieldType() and GetProviderSpecificFieldType() to report the correct CLR type for vector(float32) columns (matching how GetValue() already materializes the value as SqlVector<float>), addressing issue #4104.

Changes:

  • Update SqlDataReader to return typeof(SqlVector<float>) for SqlDbTypeExtensions.Vector (float32) in GetFieldTypeInternal.
  • Apply the same correction to GetProviderSpecificFieldTypeInternal.
  • Add a manual test covering both APIs for a vector(float32) column.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs Special-cases Vector metadata so GetFieldType/GetProviderSpecificFieldType report SqlVector<float> instead of the metatype’s default byte[].
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs Adds test coverage to validate the updated type reporting and alignment with GetValue().

Comment thread src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs Outdated
Comment thread src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs Outdated
Comment thread src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs Outdated
@rhuijben
Copy link
Copy Markdown

rhuijben commented Mar 31, 2026

Shouldn't this be fixed on the SqlBuffer layer. It already identifies SqlVector and does the deserialization.

Something like

diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
index dc1abf1c..04683c3c 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
@@ -1136,8 +1136,16 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.String:
                         return String;
                     case StorageType.SqlBinary:
-                    case StorageType.Vector:
                         return ByteArray;
+                    case StorageType.Vector:
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return GetSqlVector<float>();
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
                     case StorageType.SqlCachedBuffer:
                         {
                             // If we have a CachedBuffer, it's because it's an XMLTYPE column
@@ -1213,6 +1221,15 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.Json:
                         return typeof(SqlJson);
                         // Time Date DateTime2 and DateTimeOffset have no direct Sql type to contain them
+                    case StorageType.Vector:
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return typeof(SqlVector<float>);
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
                 }
             }
             else
@@ -1262,7 +1279,14 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.Json:
                         return typeof(string);
                     case StorageType.Vector:
-                        return typeof(byte[]);
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return typeof(SqlVector<float>);
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
 #if NET
                     case StorageType.Time:
                         return typeof(TimeOnly);

I think this last hunk is the current issue. The type returned should be retrievable.

Not sure if some of this should be extracted to a helper function or something though. And more than just Float32 could be supported these days, like Half Precision Float Vectors

@apoorvdeshmukh apoorvdeshmukh marked this pull request as draft March 31, 2026 10:30
@mdaigle mdaigle modified the milestones: 7.0.1, 7.1.0-preview1 Apr 1, 2026
@mdaigle mdaigle added the Hotfix 7.0.1 When this PR merges, automatically open a PR to cherry-pick to the 7.0.1 branch label Apr 1, 2026
@mdaigle mdaigle moved this from To triage to In progress in SqlClient Board Apr 1, 2026
@apoorvdeshmukh
Copy link
Copy Markdown
Contributor Author

Shouldn't this be fixed on the SqlBuffer layer. It already identifies SqlVector and does the deserialization.

Something like

diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
index dc1abf1c..04683c3c 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
@@ -1136,8 +1136,16 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.String:
                         return String;
                     case StorageType.SqlBinary:
-                    case StorageType.Vector:
                         return ByteArray;
+                    case StorageType.Vector:
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return GetSqlVector<float>();
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
                     case StorageType.SqlCachedBuffer:
                         {
                             // If we have a CachedBuffer, it's because it's an XMLTYPE column
@@ -1213,6 +1221,15 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.Json:
                         return typeof(SqlJson);
                         // Time Date DateTime2 and DateTimeOffset have no direct Sql type to contain them
+                    case StorageType.Vector:
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return typeof(SqlVector<float>);
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
                 }
             }
             else
@@ -1262,7 +1279,14 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.Json:
                         return typeof(string);
                     case StorageType.Vector:
-                        return typeof(byte[]);
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return typeof(SqlVector<float>);
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
 #if NET
                     case StorageType.Time:
                         return typeof(TimeOnly);

I think this last hunk is the current issue. The type returned should be retrievable.

Not sure if some of this should be extracted to a helper function or something though. And more than just Float32 could be supported these days, like Half Precision Float Vectors

Shouldn't this be fixed on the SqlBuffer layer. It already identifies SqlVector and does the deserialization.

Something like

diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
index dc1abf1c..04683c3c 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
@@ -1136,8 +1136,16 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.String:
                         return String;
                     case StorageType.SqlBinary:
-                    case StorageType.Vector:
                         return ByteArray;
+                    case StorageType.Vector:
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return GetSqlVector<float>();
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
                     case StorageType.SqlCachedBuffer:
                         {
                             // If we have a CachedBuffer, it's because it's an XMLTYPE column
@@ -1213,6 +1221,15 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.Json:
                         return typeof(SqlJson);
                         // Time Date DateTime2 and DateTimeOffset have no direct Sql type to contain them
+                    case StorageType.Vector:
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return typeof(SqlVector<float>);
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
                 }
             }
             else
@@ -1262,7 +1279,14 @@ namespace Microsoft.Data.SqlClient
                     case StorageType.Json:
                         return typeof(string);
                     case StorageType.Vector:
-                        return typeof(byte[]);
+                        var elementType = (MetaType.SqlVectorElementType)_value._vectorInfo._elementType;
+                        switch (elementType)
+                        {
+                            case MetaType.SqlVectorElementType.Float32:
+                                return typeof(SqlVector<float>);
+                            default:
+                                throw SQL.VectorTypeNotSupported(elementType.ToString());
+                        }
 #if NET
                     case StorageType.Time:
                         return typeof(TimeOnly);

I think this last hunk is the current issue. The type returned should be retrievable.

Not sure if some of this should be extracted to a helper function or something though. And more than just Float32 could be supported these days, like Half Precision Float Vectors

Thanks for the detailed analysis @rhuijben! You're right that SqlBuffer.GetTypeFromStorageType(isSqlType: false) returning typeof(byte[]) for StorageType.Vector is inconsistent with how other APIs surface vector data. However, GetFieldType()/GetProviderSpecificFieldType() work from column metadata (available before Read()), not from SqlBuffer (populated after Read()), so the SqlDataReader fix is needed regardless.

And yes, support for Half precision vectors is coming soon in SqlClient. :)

@apoorvdeshmukh apoorvdeshmukh marked this pull request as ready for review April 2, 2026 14:11
Copilot AI review requested due to automatic review settings April 2, 2026 14:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 2, 2026

Codecov Report

❌ Patch coverage is 93.75000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 66.55%. Comparing base (60d4b92) to head (547705d).
⚠️ Report is 19 commits behind head on main.

Files with missing lines Patch % Lines
...ient/src/Microsoft/Data/SqlClient/SqlDataReader.cs 93.75% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (60d4b92) and HEAD (547705d). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (60d4b92) HEAD (547705d)
CI-SqlClient 1 0
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4105      +/-   ##
==========================================
- Coverage   73.22%   66.55%   -6.67%     
==========================================
  Files         280      274       -6     
  Lines       43000    65798   +22798     
==========================================
+ Hits        31486    43794   +12308     
- Misses      11514    22004   +10490     
Flag Coverage Δ
CI-SqlClient ?
PR-SqlClient-Project 66.55% <93.75%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@paulmedynski paulmedynski self-assigned this Apr 2, 2026
@apoorvdeshmukh apoorvdeshmukh merged commit 26f5c66 into main Apr 7, 2026
301 checks passed
This was referenced Apr 27, 2026
Ghostdog02 pushed a commit to Ghostdog02/Lexiq that referenced this pull request Apr 27, 2026
Updated [DotNetEnv](https://github.com/tonerdo/dotnet-env) from 3.1.1 to
3.2.0.

<details>
<summary>Release notes</summary>

_Sourced from [DotNetEnv's
releases](https://github.com/tonerdo/dotnet-env/releases)._

## 3.2.0

- Switch parsing to Superpower (from Sprache)
- Fix utf8 parsing
- Interpolated variables parsing


Commits viewable in [compare
view](tonerdo/dotnet-env@v3.1.1...v3.2.0).
</details>

Updated
[Microsoft.AspNetCore.Authentication.Google](https://github.com/dotnet/dotnet)
from 10.0.6 to 10.0.7.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Authentication.Google's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated [Microsoft.Data.SqlClient](https://github.com/dotnet/sqlclient)
from 7.0.0 to 7.0.1.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Data.SqlClient's
releases](https://github.com/dotnet/sqlclient/releases)._

## 7.0.1

This update brings the following changes since the
[7.0.0](https://github.com/dotnet/SqlClient/blob/release/7.0/release-notes/7.0/7.0.0.md)
release:

### Fixed

- Fixed `SqlBulkCopy` failing on SQL Server 2016 with `Invalid column
name 'graph_type'` error. The column metadata query now uses dynamic SQL
so that references to the `graph_type` column (introduced in SQL Server
2017) are not compiled on older versions that lack the column.
([#​3714](dotnet/SqlClient#3714),
[#​4092](dotnet/SqlClient#4092),
[#​4147](dotnet/SqlClient#4147))

- Fixed `SqlBulkCopy` failing on Azure Synapse Analytics dedicated SQL
pools. The column-list query previously used a variable-assignment
pattern that Synapse does not support; it now uses `STRING_AGG` when
targeting Synapse (engine edition 6) and falls back to the
variable-assignment approach for SQL Server 2016 compatibility.
([#​4149](dotnet/SqlClient#4149),
[#​4176](dotnet/SqlClient#4176),
[#​4182](dotnet/SqlClient#4182))

- Fixed `SqlDataReader.GetFieldType()` and
`GetProviderSpecificFieldType()` returning `typeof(byte[])` instead of
`typeof(SqlVector<float>)` for vector float32 columns. The methods now
follow the same type-determination logic as `GetValue()`.
([#​4104](dotnet/SqlClient#4104),
[#​4105](dotnet/SqlClient#4105),
[#​4152](dotnet/SqlClient#4152))

- Added missing `System.Data.Common` (v4.3.0) NuGet package dependency
for .NET Framework consumers. The inbox `System.Data.Common` assembly on
.NET Framework predates APIs such as `IDbColumnSchemaGenerator`; without
the explicit NuGet dependency, consumers encountered `CS0012`
compilation errors when using these types through
`Microsoft.Data.SqlClient`.
([#​4063](dotnet/SqlClient#4063),
[#​4074](dotnet/SqlClient#4074))

### Changed

- Enabled the User Agent TDS feature extension unconditionally. The
`Switch.Microsoft.Data.SqlClient.EnableUserAgent` AppContext switch has
been removed; the driver now always sends User Agent information during
login. ([#​4124](dotnet/SqlClient#4124),
[#​4154](dotnet/SqlClient#4154))

- Added type forwards from the core `Microsoft.Data.SqlClient` assembly
to public types that were moved to the
`Microsoft.Data.SqlClient.Extensions.Abstractions` package:
`SqlAuthenticationMethod`, `SqlAuthenticationParameters`,
`SqlAuthenticationProvider`, `SqlAuthenticationProviderException`, and
`SqlAuthenticationToken`. This ensures binary compatibility for
assemblies compiled against earlier versions of
`Microsoft.Data.SqlClient` where these types lived in the core assembly.
([#​4067](dotnet/SqlClient#4067),
[#​4117](dotnet/SqlClient#4117))

- Fixed API documentation include paths and duplicate doc snippets.
([#​4084](dotnet/SqlClient#4084),
[#​4086](dotnet/SqlClient#4086),
[#​4107](dotnet/SqlClient#4107),
[#​4161](dotnet/SqlClient#4161))

## Contributors

We thank the following public contributors. Their efforts toward this
project are very much appreciated.

- [edwardneal](https://github.com/edwardneal)

## Target Platform Support

- .NET Framework 4.6.2+ (Windows x86, Windows x64, Windows ARM64)
- .NET 8.0+ (Windows x86, Windows x64, Windows ARM, Windows ARM64,
Linux, macOS)

### Dependencies

#### .NET 9.0

- Microsoft.Bcl.Cryptography 9.0.13
- Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0
- Microsoft.Data.SqlClient.Internal.Logging 1.0.0
- Microsoft.Data.SqlClient.SNI.runtime 6.0.2
- Microsoft.Extensions.Caching.Memory 9.0.13
- Microsoft.IdentityModel.JsonWebTokens 8.16.0
- Microsoft.IdentityModel.Protocols.OpenIdConnect 8.16.0
- Microsoft.SqlServer.Server 1.0.0
- System.Configuration.ConfigurationManager 9.0.13
- System.Security.Cryptography.Pkcs 9.0.13

#### .NET 8.0

- Microsoft.Bcl.Cryptography 8.0.0
- Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0
 ... (truncated)

Commits viewable in [compare
view](dotnet/SqlClient@v7.0.0...v7.0.1).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Hotfix 6.1.5 Hotfix 7.0.1 When this PR merges, automatically open a PR to cherry-pick to the 7.0.1 branch

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

GetFieldType() does not Return SqlVector<float>

5 participants