From 335e7dde1c5e11ec83b4cd46bd78e7ad798890e8 Mon Sep 17 00:00:00 2001 From: Apoorv Deshmukh Date: Tue, 31 Mar 2026 15:18:01 +0530 Subject: [PATCH 1/3] Return correct type for GetFieldType and GetProviderSpecificFieldType --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 22 ++++++++++++++ .../VectorTest/NativeVectorFloat32Tests.cs | 29 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 8806df9432..03f3c1626e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -1307,6 +1307,17 @@ private Type GetFieldTypeInternal(_SqlMetaData metaData) Connection.CheckGetExtendedUDTInfo(metaData, false); fieldType = metaData.udt?.Type; } + else if (metaData.type == SqlDbTypeExtensions.Vector) + { + switch (metaData.scale) + { + case (byte)MetaType.SqlVectorElementType.Float32: + fieldType = typeof(SqlVector); + break; + default: + throw SQL.VectorTypeNotSupported(metaData.scale.ToString()); + } + } else { // For all other types, including Xml - use data in MetaType. if (metaData.cipherMD != null) @@ -1422,6 +1433,17 @@ private Type GetProviderSpecificFieldTypeInternal(_SqlMetaData metaData) Connection.CheckGetExtendedUDTInfo(metaData, false); providerSpecificFieldType = metaData.udt?.Type; } + else if (metaData.type == SqlDbTypeExtensions.Vector) + { + switch (metaData.scale) + { + case (byte)MetaType.SqlVectorElementType.Float32: + providerSpecificFieldType = typeof(SqlVector); + break; + default: + throw SQL.VectorTypeNotSupported(metaData.scale.ToString()); + } + } else { // For all other types, including Xml - use data in MetaType. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs index 2ff72bba06..260542b35a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs @@ -561,6 +561,35 @@ public async Task TestBulkCopyFromSqlTableAsync(int bulkCopySourceMode) Assert.Equal(VectorFloat32TestData.testData.Length, vector.Length); } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] + public void TestGetFieldTypeReturnsSqlVectorForVectorColumn() + { + using var connection = new SqlConnection(s_connectionString); + connection.Open(); + + // Insert a row so we can query it + using (var insertCmd = new SqlCommand(s_insertCmdString, connection)) + { + var param = insertCmd.Parameters.Add(s_vectorParamName, SqlDbTypeExtensions.Vector); + param.Value = new SqlVector(VectorFloat32TestData.testData); + insertCmd.ExecuteNonQuery(); + } + + using var selectCmd = new SqlCommand(s_selectCmdString, connection); + using var reader = selectCmd.ExecuteReader(); + + // Verify GetFieldType returns SqlVector for the vector column + Assert.Equal(typeof(SqlVector), reader.GetFieldType(0)); + + // Verify GetProviderSpecificFieldType also returns SqlVector + Assert.Equal(typeof(SqlVector), reader.GetProviderSpecificFieldType(0)); + + // Verify that GetValue returns an instance consistent with GetFieldType + Assert.True(reader.Read(), "No data found in the table."); + object value = reader.GetValue(0); + Assert.IsType>(value); + } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] public void TestInsertVectorsFloat32WithPrepare() { From 1d2ea6e920ec18b0667d9e47156227ec28682e20 Mon Sep 17 00:00:00 2001 From: Apoorv Deshmukh Date: Thu, 2 Apr 2026 19:35:08 +0530 Subject: [PATCH 2/3] Address review comments --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 03f3c1626e..d17e668b4b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -1309,14 +1309,7 @@ private Type GetFieldTypeInternal(_SqlMetaData metaData) } else if (metaData.type == SqlDbTypeExtensions.Vector) { - switch (metaData.scale) - { - case (byte)MetaType.SqlVectorElementType.Float32: - fieldType = typeof(SqlVector); - break; - default: - throw SQL.VectorTypeNotSupported(metaData.scale.ToString()); - } + fieldType = GetVectorFieldType(metaData.scale); } else { // For all other types, including Xml - use data in MetaType. @@ -1340,6 +1333,19 @@ private Type GetFieldTypeInternal(_SqlMetaData metaData) return fieldType; } +#if !NETFRAMEWORK + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] +#endif + private static Type GetVectorFieldType(byte scale) + { + MetaType.SqlVectorElementType elementType = (MetaType.SqlVectorElementType)scale; + return elementType switch + { + MetaType.SqlVectorElementType.Float32 => typeof(SqlVector), + _ => throw SQL.VectorTypeNotSupported(elementType.ToString()), + }; + } + virtual internal int GetLocaleId(int i) { _SqlMetaData sqlMetaData = MetaData[i]; @@ -1435,14 +1441,7 @@ private Type GetProviderSpecificFieldTypeInternal(_SqlMetaData metaData) } else if (metaData.type == SqlDbTypeExtensions.Vector) { - switch (metaData.scale) - { - case (byte)MetaType.SqlVectorElementType.Float32: - providerSpecificFieldType = typeof(SqlVector); - break; - default: - throw SQL.VectorTypeNotSupported(metaData.scale.ToString()); - } + providerSpecificFieldType = GetVectorFieldType(metaData.scale); } else { From 547705d8615f07f814a80b877e7d565b24b70c8f Mon Sep 17 00:00:00 2001 From: Apoorv Deshmukh Date: Mon, 6 Apr 2026 19:30:29 +0530 Subject: [PATCH 3/3] Add additional assertions --- .../src/Microsoft/Data/SqlClient/SqlDataReader.cs | 4 ++-- .../SQL/VectorTest/NativeVectorFloat32Tests.cs | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index d17e668b4b..bf1f38da00 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -1336,9 +1336,9 @@ private Type GetFieldTypeInternal(_SqlMetaData metaData) #if !NETFRAMEWORK [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] #endif - private static Type GetVectorFieldType(byte scale) + private static Type GetVectorFieldType(byte vectorElementType) { - MetaType.SqlVectorElementType elementType = (MetaType.SqlVectorElementType)scale; + MetaType.SqlVectorElementType elementType = (MetaType.SqlVectorElementType)vectorElementType; return elementType switch { MetaType.SqlVectorElementType.Float32 => typeof(SqlVector), diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs index 260542b35a..0a86534112 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs @@ -29,19 +29,19 @@ public static IEnumerable GetVectorFloat32TestData() yield return new object[] { 3, new SqlVector(testData), testData, vectorColumnLength }; yield return new object[] { 4, new SqlVector(testData), testData, vectorColumnLength }; - // Pattern 1-4 with SqlVector(n) + // Pattern 1-4 with SqlVector(n) yield return new object[] { 1, SqlVector.CreateNull(vectorColumnLength), Array.Empty(), vectorColumnLength }; yield return new object[] { 2, SqlVector.CreateNull(vectorColumnLength), Array.Empty(), vectorColumnLength }; yield return new object[] { 3, SqlVector.CreateNull(vectorColumnLength), Array.Empty(), vectorColumnLength }; yield return new object[] { 4, SqlVector.CreateNull(vectorColumnLength), Array.Empty(), vectorColumnLength }; - // Pattern 1-4 with DBNull + // Pattern 1-4 with DBNull yield return new object[] { 1, DBNull.Value, Array.Empty(), vectorColumnLength }; yield return new object[] { 2, DBNull.Value, Array.Empty(), vectorColumnLength }; yield return new object[] { 3, DBNull.Value, Array.Empty(), vectorColumnLength }; yield return new object[] { 4, DBNull.Value, Array.Empty(), vectorColumnLength }; - // Pattern 1-4 with SqlVector.Null + // Pattern 1-4 with SqlVector.Null yield return new object[] { 1, SqlVector.Null, Array.Empty(), vectorColumnLength }; // Following scenario is not supported in SqlClient. @@ -588,6 +588,12 @@ public void TestGetFieldTypeReturnsSqlVectorForVectorColumn() Assert.True(reader.Read(), "No data found in the table."); object value = reader.GetValue(0); Assert.IsType>(value); + Assert.Equal(VectorFloat32TestData.testData, ((SqlVector)value).Memory.ToArray()); + + // Verify GetFieldValue> returns the correct typed value + SqlVector typedValue = reader.GetFieldValue>(0); + Assert.IsType>(typedValue); + Assert.Equal(VectorFloat32TestData.testData, typedValue.Memory.ToArray()); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))]