diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 916b9ac04d..973c081999 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -87,11 +87,14 @@ public static class DataTestUtility // SQL server Version private static string s_sQLServerVersion = string.Empty; - private static bool s_isTDS8Supported; //SQL Server EngineEdition private static string s_sqlServerEngineEdition; + // SQL Server capabilities + private static bool? s_isDataClassificationSupported; + private static bool? s_isVectorSupported; + // Azure Synapse EngineEditionId == 6 // More could be read at https://learn.microsoft.com/en-us/sql/t-sql/functions/serverproperty-transact-sql?view=sql-server-ver16#propertyname public static bool IsAzureSynapse @@ -129,17 +132,28 @@ public static string SQLServerVersion } // Is TDS8 supported - public static bool IsTDS8Supported - { - get - { - if (!string.IsNullOrEmpty(TCPConnectionString)) - { - s_isTDS8Supported = GetSQLServerStatusOnTDS8(TCPConnectionString); - } - return s_isTDS8Supported; - } - } + public static bool IsTDS8Supported => + IsTCPConnStringSetup() && + GetSQLServerStatusOnTDS8(TCPConnectionString); + + /// + /// Checks if object SYS.SENSITIVITY_CLASSIFICATIONS exists in SQL Server + /// + /// True, if target SQL Server supports Data Classification + public static bool IsDataClassificationSupported => + s_isDataClassificationSupported ??= IsTCPConnStringSetup() && + IsObjectPresent("SYS.SENSITIVITY_CLASSIFICATIONS"); + + /// + /// Determines whether the SQL Server supports the 'vector' data type. + /// + /// This method attempts to connect to the SQL Server and check for the existence of the + /// 'vector' data type. If a connection cannot be established or an error occurs during the query, the method + /// returns . + /// if the 'vector' data type is supported; otherwise, . + public static bool IsSqlVectorSupported => + s_isVectorSupported ??= IsTCPConnStringSetup() && + IsTypePresent("vector"); static DataTestUtility() { @@ -309,7 +323,7 @@ public static string GetSqlServerProperty(string connectionString, string proper return propertyValue; } - public static bool GetSQLServerStatusOnTDS8(string connectionString) + private static bool GetSQLServerStatusOnTDS8(string connectionString) { bool isTDS8Supported = false; SqlConnectionStringBuilder builder = new(connectionString) @@ -351,6 +365,28 @@ public static bool IsDatabasePresent(string name) return present; } + public static bool IsObjectPresent(string objectName) + { + using SqlConnection connection = new(TCPConnectionString); + using SqlCommand command = new("SELECT OBJECT_ID(@name)", connection); + + connection.Open(); + command.Parameters.AddWithValue("@name", objectName); + + return command.ExecuteScalar() is not DBNull; + } + + public static bool IsTypePresent(string typeName) + { + using SqlConnection connection = new(TCPConnectionString); + using SqlCommand command = new("SELECT COUNT(1) FROM SYS.TYPES WHERE [name] = @name", connection); + + connection.Open(); + command.Parameters.AddWithValue("@name", typeName); + + return (int)command.ExecuteScalar() > 0; + } + public static bool IsAdmin { get @@ -362,32 +398,6 @@ public static bool IsAdmin } } - /// - /// Checks if object SYS.SENSITIVITY_CLASSIFICATIONS exists in SQL Server - /// - /// True, if target SQL Server supports Data Classification - public static bool IsSupportedDataClassification() - { - try - { - using (var connection = new SqlConnection(TCPConnectionString)) - using (var command = new SqlCommand("SELECT * FROM SYS.SENSITIVITY_CLASSIFICATIONS", connection)) - { - connection.Open(); - command.ExecuteNonQuery(); - } - } - catch (SqlException e) - { - // Check for Error 208: Invalid Object Name - if (e.Errors != null && e.Errors[0].Number == 208) - { - return false; - } - } - return true; - } - public static bool IsDNSCachingSetup() => !string.IsNullOrEmpty(DNSCachingConnString); // Synapse: Always Encrypted is not supported with Azure Synapse. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs index 3e7076d52d..ce5174b6ca 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs @@ -15,7 +15,7 @@ public static class DataClassificationTest private static string s_tableName; // Synapse: Azure Synapse does not support RANK - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.IsSupportedDataClassification))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.IsDataClassificationSupported))] public static void TestDataClassificationResultSetRank() { s_tableName = DataTestUtility.GetLongName("DC"); @@ -25,7 +25,7 @@ public static void TestDataClassificationResultSetRank() try { sqlConnection.Open(); - Assert.True(DataTestUtility.IsSupportedDataClassification()); + Assert.True(DataTestUtility.IsDataClassificationSupported); CreateTable(sqlCommand); AddSensitivity(sqlCommand, rankEnabled: true); InsertData(sqlCommand); @@ -38,7 +38,7 @@ public static void TestDataClassificationResultSetRank() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSupportedDataClassification))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsDataClassificationSupported))] public static void TestDataClassificationResultSet() { s_tableName = DataTestUtility.GetLongName("DC"); @@ -48,7 +48,7 @@ public static void TestDataClassificationResultSet() try { sqlConnection.Open(); - Assert.True(DataTestUtility.IsSupportedDataClassification()); + Assert.True(DataTestUtility.IsDataClassificationSupported); CreateTable(sqlCommand); AddSensitivity(sqlCommand); InsertData(sqlCommand); @@ -219,7 +219,7 @@ private static void InsertData(SqlCommand sqlCommand) sqlCommand.ExecuteNonQuery(); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSupportedDataClassification))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsDataClassificationSupported))] public static void TestDataClassificationBulkCopy() { var data = new DataTable("Company"); 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 85dd19fab1..2ff72bba06 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs @@ -17,7 +17,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.VectorTest public static class VectorFloat32TestData { public const int VectorHeaderSize = 8; - public static float[] testData = new float[] { 1.1f, 2.2f, 3.3f }; + public static float[] testData = new float[] { 1.1f, 2.2f, 3.3f, 1.01f, float.MinValue, -0.0f }; public static int vectorColumnLength = testData.Length; // Incorrect size for SqlParameter.Size public static int IncorrectParamSize = 3234; @@ -59,16 +59,17 @@ public sealed class NativeVectorFloat32Tests : IDisposable private static readonly string s_connectionString = ManualTesting.Tests.DataTestUtility.TCPConnectionString; private static readonly string s_tableName = DataTestUtility.GetShortName("VectorTestTable"); private static readonly string s_bulkCopySrcTableName = DataTestUtility.GetShortName("VectorBulkCopyTestTable"); - private static readonly string s_bulkCopySrcTableDef = $@"(Id INT PRIMARY KEY IDENTITY, VectorData vector(3) NULL)"; - private static readonly string s_tableDefinition = $@"(Id INT PRIMARY KEY IDENTITY, VectorData vector(3) NULL)"; + private static readonly int s_vectorDimensions = VectorFloat32TestData.vectorColumnLength; + private static readonly string s_bulkCopySrcTableDef = $@"(Id INT PRIMARY KEY IDENTITY, VectorData vector({s_vectorDimensions}) NULL)"; + private static readonly string s_tableDefinition = $@"(Id INT PRIMARY KEY IDENTITY, VectorData vector({s_vectorDimensions}) NULL)"; private static readonly string s_selectCmdString = $"SELECT VectorData FROM {s_tableName} ORDER BY Id DESC"; private static readonly string s_insertCmdString = $"INSERT INTO {s_tableName} (VectorData) VALUES (@VectorData)"; private static readonly string s_vectorParamName = $"@VectorData"; private static readonly string s_outputVectorParamName = $"@OutputVectorData"; private static readonly string s_storedProcName = DataTestUtility.GetShortName("VectorsAsVarcharSp"); private static readonly string s_storedProcBody = $@" - {s_vectorParamName} vector(3), -- Input: Serialized float[] as JSON string - {s_outputVectorParamName} vector(3) OUTPUT -- Output: Echoed back from latest inserted row + {s_vectorParamName} vector({s_vectorDimensions}), -- Input: Serialized float[] as JSON string + {s_outputVectorParamName} vector({s_vectorDimensions}) OUTPUT -- Output: Echoed back from latest inserted row AS BEGIN SET NOCOUNT ON; @@ -147,7 +148,7 @@ private void ValidateInsertedData(SqlConnection connection, float[] expectedData } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] [MemberData(nameof(VectorFloat32TestData.GetVectorFloat32TestData), MemberType = typeof(VectorFloat32TestData), DisableDiscoveryEnumeration = true)] public void TestSqlVectorFloat32ParameterInsertionAndReads( int pattern, @@ -213,7 +214,7 @@ private async Task ValidateInsertedDataAsync(SqlConnection connection, float[] e } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] [MemberData(nameof(VectorFloat32TestData.GetVectorFloat32TestData), MemberType = typeof(VectorFloat32TestData), DisableDiscoveryEnumeration = true)] public async Task TestSqlVectorFloat32ParameterInsertionAndReadsAsync( int pattern, @@ -247,7 +248,7 @@ public async Task TestSqlVectorFloat32ParameterInsertionAndReadsAsync( await ValidateInsertedDataAsync(conn, expectedValues, expectedLength); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] [MemberData(nameof(VectorFloat32TestData.GetVectorFloat32TestData), MemberType = typeof(VectorFloat32TestData), DisableDiscoveryEnumeration = true)] public void TestStoredProcParamsForVectorFloat32( int pattern, @@ -304,7 +305,7 @@ public void TestStoredProcParamsForVectorFloat32( Assert.Throws(() => command.ExecuteNonQuery()); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] [MemberData(nameof(VectorFloat32TestData.GetVectorFloat32TestData), MemberType = typeof(VectorFloat32TestData), DisableDiscoveryEnumeration = true)] public async Task TestStoredProcParamsForVectorFloat32Async( int pattern, @@ -361,7 +362,7 @@ public async Task TestStoredProcParamsForVectorFloat32Async( await Assert.ThrowsAsync(async () => await command.ExecuteNonQueryAsync()); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] [InlineData(1)] [InlineData(2)] public void TestBulkCopyFromSqlTable(int bulkCopySourceMode) @@ -460,7 +461,7 @@ public void TestBulkCopyFromSqlTable(int bulkCopySourceMode) Assert.Equal(VectorFloat32TestData.testData.Length, ((SqlVector)verifyReader.GetSqlVector(0)).Length); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] [InlineData(1)] [InlineData(2)] public async Task TestBulkCopyFromSqlTableAsync(int bulkCopySourceMode) @@ -560,7 +561,7 @@ public async Task TestBulkCopyFromSqlTableAsync(int bulkCopySourceMode) Assert.Equal(VectorFloat32TestData.testData.Length, vector.Length); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsSqlVectorSupported))] public void TestInsertVectorsFloat32WithPrepare() { SqlConnection conn = new SqlConnection(s_connectionString); @@ -571,7 +572,7 @@ public void TestInsertVectorsFloat32WithPrepare() command.Prepare(); for (int i = 0; i < 10; i++) { - vectorParam.Value = new SqlVector(new float[] { i + 0.1f, i + 0.2f, i + 0.3f }); + vectorParam.Value = new SqlVector(new float[] { i + 0.1f, i + 0.2f, i + 0.3f, i + 0.4f, i + 0.5f, i + 0.6f }); command.ExecuteNonQuery(); } SqlCommand validateCommand = new SqlCommand($"SELECT VectorData FROM {s_tableName}", conn); @@ -579,7 +580,7 @@ public void TestInsertVectorsFloat32WithPrepare() int rowcnt = 0; while (reader.Read()) { - float[] expectedData = new float[] { rowcnt + 0.1f, rowcnt + 0.2f, rowcnt + 0.3f }; + float[] expectedData = new float[] { rowcnt + 0.1f, rowcnt + 0.2f, rowcnt + 0.3f, rowcnt + 0.4f, rowcnt + 0.5f, rowcnt + 0.6f }; float[] dbData = reader.GetSqlVector(0).Memory.ToArray(); Assert.Equal(expectedData, dbData); rowcnt++; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs index 4f587274a7..b3f42a5617 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs @@ -81,7 +81,7 @@ private void ValidateInsertedData(SqlConnection connection, float[] expectedData } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public void TestVectorDataInsertionAsVarchar() { float[] data = { 1.1f, 2.2f, 3.3f }; @@ -173,7 +173,7 @@ private async Task ValidateInsertedDataAsync(SqlConnection connection, float[] e } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public async Task TestVectorParameterInitializationAsync() { float[] data = { 1.1f, 2.2f, 3.3f }; @@ -245,7 +245,7 @@ public async Task TestVectorParameterInitializationAsync() await ValidateInsertedDataAsync(conn, null); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public void TestVectorDataReadsAsVarchar() { float[] data = { 1.1f, 2.2f, 3.3f }; @@ -302,7 +302,7 @@ public void TestVectorDataReadsAsVarchar() Assert.Throws(() => reader.GetFieldValue(0)); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public async Task TestVectorDataReadsAsVarcharAsync() { float[] data = { 1.1f, 2.2f, 3.3f }; @@ -359,7 +359,7 @@ public async Task TestVectorDataReadsAsVarcharAsync() await Assert.ThrowsAsync(async () => await reader2.GetFieldValueAsync(0)); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public void TestStoredProcParamsForVectorAsVarchar() { // Test data @@ -405,7 +405,7 @@ public void TestStoredProcParamsForVectorAsVarchar() Assert.True(outputParam.Value == DBNull.Value); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public async Task TestStoredProcParamsForVectorAsVarcharAsync() { // Test data @@ -456,7 +456,7 @@ public async Task TestStoredProcParamsForVectorAsVarcharAsync() Assert.True(outputParam.Value == DBNull.Value); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public void TestSqlBulkCopyForVectorAsVarchar() { //Setup source with test data and create destination table for bulkcopy. @@ -521,7 +521,7 @@ public void TestSqlBulkCopyForVectorAsVarchar() Assert.True(verifyReader.IsDBNull(0)); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public async Task TestSqlBulkCopyForVectorAsVarcharAsync() { //Setup source with test data and create destination table for bulkcopy. @@ -586,7 +586,7 @@ public async Task TestSqlBulkCopyForVectorAsVarcharAsync() Assert.True(await verifyReader.IsDBNullAsync(0)); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSqlVectorSupported))] public void TestInsertVectorsAsVarcharWithPrepare() { SqlConnection conn = new SqlConnection(s_connectionString);