Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is still querying the server every time this property is accessed. Can you query the server once and store the result for all future access?

This would be a great use of the new field keyword if we were already using the .NET 10 SDK:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/field

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about doing that, but the behaviour wouldn't be correct unfortunately. This is only used in the CertificateTest class; the constructor for this class runs the PowerShell script GenerateSelfSignedCertificate.ps1.

That PowerShell script changes whether TDS8 is supported; it generates a new certificate with the right ACL for the SQL Server service to use it, forces Windows to trust it, then configures SQL Server to use it. The value of the check would vary depending upon whether anything else has read the property before that caching occurred.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand. The TCPConnString is constant for all tests in the test run, so the server is supports TDS8 for all tests in the run, or it doesn't. I'm not sure how that affects anything in the ps1 script - I don't see it performing any checks for TDS8.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TCPConnString is constant, but the tests in CertificateTest only run when that connection string points to localhost. The script is run before the tests, and it dynamically reconfigures the local SQL Server instance to meet all of the prerequisites for TDS8 - if we make IsTDS8Supported cache its result and a future test runs prior to a test from CertificateTest, we could return a stale value.

That can't happen right now (CertificateTest is the only test which uses this condition) but it'd cause a problem if we use it from any other test.


/// <summary>
/// Checks if object SYS.SENSITIVITY_CLASSIFICATIONS exists in SQL Server
/// </summary>
/// <returns>True, if target SQL Server supports Data Classification</returns>
public static bool IsDataClassificationSupported =>
s_isDataClassificationSupported ??= IsTCPConnStringSetup() &&
IsObjectPresent("SYS.SENSITIVITY_CLASSIFICATIONS");

/// <summary>
/// Determines whether the SQL Server supports the 'vector' data type.
/// </summary>
/// <remarks>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 <see langword="false"/>.</remarks>
/// <returns><see langword="true"/> if the 'vector' data type is supported; otherwise, <see langword="false"/>.</returns>
public static bool IsSqlVectorSupported =>
s_isVectorSupported ??= IsTCPConnStringSetup() &&
IsTypePresent("vector");

static DataTestUtility()
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -362,32 +398,6 @@ public static bool IsAdmin
}
}

/// <summary>
/// Checks if object SYS.SENSITIVITY_CLASSIFICATIONS exists in SQL Server
/// </summary>
/// <returns>True, if target SQL Server supports Data Classification</returns>
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);
Expand All @@ -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");
Expand All @@ -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);
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -304,7 +305,7 @@ public void TestStoredProcParamsForVectorFloat32(
Assert.Throws<InvalidOperationException>(() => 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,
Expand Down Expand Up @@ -361,7 +362,7 @@ public async Task TestStoredProcParamsForVectorFloat32Async(
await Assert.ThrowsAsync<InvalidOperationException>(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)
Expand Down Expand Up @@ -460,7 +461,7 @@ public void TestBulkCopyFromSqlTable(int bulkCopySourceMode)
Assert.Equal(VectorFloat32TestData.testData.Length, ((SqlVector<float>)verifyReader.GetSqlVector<float>(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)
Expand Down Expand Up @@ -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);
Expand All @@ -571,15 +572,15 @@ public void TestInsertVectorsFloat32WithPrepare()
command.Prepare();
for (int i = 0; i < 10; i++)
{
vectorParam.Value = new SqlVector<float>(new float[] { i + 0.1f, i + 0.2f, i + 0.3f });
vectorParam.Value = new SqlVector<float>(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);
using SqlDataReader reader = validateCommand.ExecuteReader();
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<float>(0).Memory.ToArray();
Assert.Equal(expectedData, dbData);
rowcnt++;
Expand Down
Loading
Loading