Skip to content

Commit f5942fd

Browse files
Add float16 vector backward compatibility tests and refactor float32 vectors as varchar(max) test suite. (#4234)
This commit refactors test suite for testing float32 vectors as varchar(max). Adds testcases for testing float16 vectors as varchar(max). Avoids test duplication and use RAII classes.
1 parent 3af6e09 commit f5942fd

5 files changed

Lines changed: 698 additions & 575 deletions

File tree

src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public static class DataTestUtility
9595
// SQL Server capabilities
9696
private static bool? s_isDataClassificationSupported;
9797
private static bool? s_isVectorSupported;
98+
private static bool? s_isVectorFloat16Supported;
9899

99100
// Azure Synapse EngineEditionId == 6
100101
// More could be read at https://learn.microsoft.com/en-us/sql/t-sql/functions/serverproperty-transact-sql?view=sql-server-ver16#propertyname
@@ -156,6 +157,68 @@ public static string SQLServerVersion
156157
s_isVectorSupported ??= IsTCPConnStringSetup() &&
157158
IsTypePresent("vector");
158159

160+
/// <summary>
161+
/// Determines whether the SQL Server supports the 'float16' base type for the 'vector' data type.
162+
/// </summary>
163+
/// <remarks>
164+
/// Probes the server by first executing
165+
/// <c>ALTER DATABASE SCOPED CONFIGURATION SET PREVIEW_FEATURES = ON;</c> and then executing
166+
/// <c>DECLARE @v AS VECTOR(5, float16) = '[1.0, 1.0, 1.0, 1.0, 1.0]'; SELECT @v;</c>.
167+
/// As a side effect, this enables preview features for the current database. If the server does not
168+
/// recognize <c>float16</c> as a vector base type, if the server does not support the required syntax,
169+
/// if the current principal lacks permission to change the database scoped configuration, or if another
170+
/// server-side error occurs while running the probe, this method returns
171+
/// <see langword="false"/>. Implies <see cref="IsSqlVectorSupported"/>.
172+
/// </remarks>
173+
/// <returns><see langword="true"/> if the 'float16' vector base type is supported; otherwise, <see langword="false"/>.</returns>
174+
public static bool IsSqlVectorFloat16Supported =>
175+
s_isVectorFloat16Supported ??= IsTCPConnStringSetup() &&
176+
IsSqlVectorSupported &&
177+
CheckVectorFloat16Supported();
178+
179+
private static bool CheckVectorFloat16Supported()
180+
{
181+
try
182+
{
183+
using SqlConnection connection = new(TCPConnectionString);
184+
// Enable preview features (required while float16 is in preview), then declare a
185+
// float16 vector and select it back. Without a negotiated float16 feature extension,
186+
// the server returns the value as a varchar(max) JSON string, which the client can
187+
// safely read. We use 1.0 (exactly representable in IEEE-754 binary16) so the
188+
// round-tripped JSON matches the input bit-for-bit. Any failure (server parse
189+
// error such as Msg 195 "'float16' is not a recognized vector base type.", lack of
190+
// permission to set PREVIEW_FEATURES, etc.) means we cannot exercise the float16
191+
// path.
192+
float[] expected = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
193+
using SqlCommand command = new(
194+
"ALTER DATABASE SCOPED CONFIGURATION SET PREVIEW_FEATURES = ON;" +
195+
"DECLARE @v AS VECTOR(5, float16) = '[1.0, 1.0, 1.0, 1.0, 1.0]'; SELECT @v;",
196+
connection);
197+
connection.Open();
198+
using SqlDataReader reader = command.ExecuteReader();
199+
if (!reader.Read())
200+
{
201+
return false;
202+
}
203+
string json = reader.GetString(0);
204+
float[] actual = System.Text.Json.JsonSerializer.Deserialize<float[]>(json);
205+
return actual != null && actual.Length == expected.Length
206+
&& actual.AsSpan().SequenceEqual(expected);
207+
}
208+
catch (SqlException)
209+
{
210+
// Server-side failure (e.g. Msg 195 "'float16' is not a recognized vector base
211+
// type.", Msg 102 syntax errors on older servers, lack of permission to set
212+
// PREVIEW_FEATURES) — float16 path is unavailable.
213+
return false;
214+
}
215+
catch (System.Text.Json.JsonException)
216+
{
217+
// Server returned a payload we can't parse as a float[] JSON array.
218+
return false;
219+
}
220+
}
221+
159222
static DataTestUtility()
160223
{
161224
Config c = Config.Load();

src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,10 @@
243243
<Compile Include="SQL\UdtTest\UdtTest2.cs" />
244244
<Compile Include="SQL\UdtTest\UdtTestHelpers.cs" />
245245
<Compile Include="SQL\Utf8SupportTest\Utf8SupportTest.cs" />
246+
<Compile Include="SQL\VectorTest\Float16VectorTypeBackwardCompatibilityTests.cs" />
246247
<Compile Include="SQL\VectorTest\NativeVectorFloat32Tests.cs" />
247248
<Compile Include="SQL\VectorTest\VectorAPIValidationTest.cs" />
249+
<Compile Include="SQL\VectorTest\VectorBackwardCompatTestBase.cs" />
248250
<Compile Include="SQL\VectorTest\VectorTypeBackwardCompatibilityTests.cs" />
249251
<Compile Include="SQL\WeakRefTest\WeakRefTest.cs" />
250252
<Compile Include="SQL\WeakRefTestYukonSpecific\WeakRefTestYukonSpecific.cs" />
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
using System.Text.Json;
7+
using System.Threading.Tasks;
8+
using Xunit;
9+
using Xunit.Abstractions;
10+
11+
namespace Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.VectorTest
12+
{
13+
/// <summary>
14+
/// Provides parameterized test data for backward compatibility tests that exchange
15+
/// float16 vector data as varchar(max) JSON strings.
16+
/// </summary>
17+
public static class Float16VarcharVectorTestData
18+
{
19+
// Values chosen to be exactly representable in IEEE-754 binary16 (float16),
20+
// so JSON round-trips through a vector(N, float16) column without precision loss.
21+
public static readonly float[] TestData = { 1.0f, 2.0f, 3.0f };
22+
23+
/// <summary>
24+
/// Generates test cases for all 4 SqlParameter construction patterns x 2 value types (non-null + null).
25+
/// Each case yields: [int pattern, string jsonOrNull, float[] expectedData]
26+
/// where jsonOrNull is null when testing DBNull insertion.
27+
/// </summary>
28+
public static IEnumerable<object[]> GetVarcharVectorInsertTestData()
29+
{
30+
string json = JsonSerializer.Serialize(TestData);
31+
32+
// Pattern 1-4 with non-null JSON value
33+
yield return new object[] { 1, json, TestData };
34+
yield return new object[] { 2, json, TestData };
35+
yield return new object[] { 3, json, TestData };
36+
yield return new object[] { 4, json, TestData };
37+
38+
// Pattern 1-4 with null value
39+
yield return new object[] { 1, null, null };
40+
yield return new object[] { 2, null, null };
41+
yield return new object[] { 3, null, null };
42+
yield return new object[] { 4, null, null };
43+
}
44+
}
45+
46+
public sealed class Float16VectorTypeBackwardCompatibilityTests : VectorBackwardCompatTestBase
47+
{
48+
public Float16VectorTypeBackwardCompatibilityTests(ITestOutputHelper output)
49+
: base(output, columnDefinition: "vector(3, float16)", namePrefix: "VectorF16")
50+
{
51+
}
52+
53+
protected override float[] GetPrepareTestValues(int i) =>
54+
new float[] { i + 1, i + 2, i + 3 };
55+
56+
#region Insert Tests
57+
58+
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorFloat16Supported))]
59+
[MemberData(nameof(Float16VarcharVectorTestData.GetVarcharVectorInsertTestData), MemberType = typeof(Float16VarcharVectorTestData), DisableDiscoveryEnumeration = true)]
60+
public void TestVectorDataInsertionAsVarchar(int pattern, string jsonValue, float[] expectedData)
61+
=> InsertAndValidateAsVarchar(pattern, jsonValue, expectedData);
62+
63+
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorFloat16Supported))]
64+
[MemberData(nameof(Float16VarcharVectorTestData.GetVarcharVectorInsertTestData), MemberType = typeof(Float16VarcharVectorTestData), DisableDiscoveryEnumeration = true)]
65+
public async Task TestVectorDataInsertionAsVarcharAsync(int pattern, string jsonValue, float[] expectedData)
66+
=> await InsertAndValidateAsVarcharAsync(pattern, jsonValue, expectedData);
67+
68+
#endregion
69+
70+
#region Stored Procedure Tests
71+
72+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorFloat16Supported))]
73+
public void TestStoredProcParamsForVectorAsVarchar()
74+
=> StoredProcRoundTrip(Float16VarcharVectorTestData.TestData);
75+
76+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorFloat16Supported))]
77+
public async Task TestStoredProcParamsForVectorAsVarcharAsync()
78+
=> await StoredProcRoundTripAsync(Float16VarcharVectorTestData.TestData);
79+
80+
#endregion
81+
82+
#region Bulk Copy Tests
83+
84+
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorFloat16Supported))]
85+
[InlineData(1)]
86+
[InlineData(2)]
87+
public void TestSqlBulkCopyForVectorAsVarchar(int bulkCopySourceMode)
88+
=> BulkCopyRoundTrip(bulkCopySourceMode, Float16VarcharVectorTestData.TestData);
89+
90+
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorFloat16Supported))]
91+
[InlineData(1)]
92+
[InlineData(2)]
93+
public async Task TestSqlBulkCopyForVectorAsVarcharAsync(int bulkCopySourceMode)
94+
=> await BulkCopyRoundTripAsync(bulkCopySourceMode, Float16VarcharVectorTestData.TestData);
95+
96+
#endregion
97+
98+
#region Prepared Statement Tests
99+
100+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorFloat16Supported))]
101+
public void TestInsertVectorsAsVarcharWithPrepare()
102+
=> PreparedInsertRoundTrip();
103+
104+
#endregion
105+
}
106+
}

0 commit comments

Comments
 (0)