From e5ed8d8dcbc51da338fe64e2654151f040b99c51 Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 18 Dec 2023 10:17:14 +0100 Subject: [PATCH 1/8] enable reading AE date as DateOnly fixes #2274 --- .../src/Microsoft/Data/SqlClient/SqlDataReader.cs | 4 ++-- .../tests/ManualTests/AlwaysEncrypted/ApiShould.cs | 7 +++++++ .../ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs | 3 +++ .../AlwaysEncrypted/TestFixtures/DatabaseHelper.cs | 12 ++++++++++++ .../TestFixtures/Setup/ApiTestTable.cs | 8 +++++--- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs index cb0f108366..5eb7d7cdeb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -2863,11 +2863,11 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met return (T)(object)data.DateTime; } #if NET6_0_OR_GREATER - else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) + else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005) { return (T)(object)data.DateOnly; } - else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) + else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005) { return (T)(object)data.TimeOnly; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 6e92b4f479..5f155ea313 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -3160,6 +3160,9 @@ public Customer(int id, string firstName, string lastName) Id = id; FirstName = firstName; LastName = lastName; +#if NET6_0_OR_GREATER + DateOfBirth = new DateOnly(1990, 1, 31); +#endif } public int Id { get; set; } @@ -3167,6 +3170,10 @@ public Customer(int id, string firstName, string lastName) public string FirstName { get; set; } public string LastName { get; set; } + +#if NET6_0_OR_GREATER + public DateOnly DateOfBirth { get; set; } +#endif } internal class TestAsyncCallBackStateObject diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs index 874c225d9f..99981f190b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs @@ -149,6 +149,9 @@ public IEnumerator GetEnumerator() yield return new object[] { connStrAE, @"select CustomerId, FirstName, LastName from [{0}] ", 3, new string[] { @"int", @"string", @"string" } }; yield return new object[] { connStrAE, @"select CustomerId, FirstName from [{0}] ", 2, new string[] { @"int", @"string" } }; yield return new object[] { connStrAE, @"select LastName from [{0}] ", 1, new string[] { @"string" }}; +#if NET6_0_OR_GREATER + yield return new object[] { connStrAE, @"select DateOfBirth from [{0}] ", 1, new string[] { @"DateOnly" } }; +#endif } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index 1ebdcfe04d..bc97da66bf 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -20,7 +20,11 @@ class DatabaseHelper internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransaction transaction, string tableName, Customer customer) { using SqlCommand sqlCommand = new( +#if NET6_0_OR_GREATER + $"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName, DateOfBirth) VALUES (@CustomerId, @FirstName, @LastName, @DateOfBirth);", +#else $"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName) VALUES (@CustomerId, @FirstName, @LastName);", +#endif connection: sqlConnection, transaction: transaction, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled); @@ -28,6 +32,9 @@ internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransact sqlCommand.Parameters.AddWithValue(@"CustomerId", customer.Id); sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName); sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName); +#if NET6_0_OR_GREATER + sqlCommand.Parameters.AddWithValue(@"DateOfBirth", customer.DateOfBirth); +#endif sqlCommand.ExecuteNonQuery(); } @@ -156,6 +163,11 @@ public static void CompareResults(SqlDataReader sqlDataReader, string[] paramete Assert.True(sqlDataReader.GetInt32(columnsRead) == 45, "FAILED: read int value does not match."); break; +#if NET6_0_OR_GREATER + case "DateOnly": + Assert.True(sqlDataReader.GetFieldValue(columnsRead) == new DateOnly(1990, 1, 31), "FAILED: read DateOnly value does not match."); + break; +#endif default: Assert.Fail("FAILED: unexpected data type."); break; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs index 8ab0421707..a4568dd407 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs @@ -26,12 +26,14 @@ public override void Create(SqlConnection sqlConnection) ( [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), - [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}') - )"; + [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}')"; +#if NET6_0_OR_GREATER + sql = sql + $",[DateOfBirth] [date] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}')"; +#endif using (SqlCommand command = sqlConnection.CreateCommand()) { - command.CommandText = sql; + command.CommandText = sql + ")"; command.ExecuteNonQuery(); } } From d7048d18a56556dec610bcbad5fa3d37da52255a Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 18 Dec 2023 12:55:53 +0100 Subject: [PATCH 2/8] Revert "enable reading AE date as DateOnly" This reverts commit e5ed8d8dcbc51da338fe64e2654151f040b99c51. --- .../src/Microsoft/Data/SqlClient/SqlDataReader.cs | 4 ++-- .../tests/ManualTests/AlwaysEncrypted/ApiShould.cs | 7 ------- .../ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs | 3 --- .../AlwaysEncrypted/TestFixtures/DatabaseHelper.cs | 12 ------------ .../TestFixtures/Setup/ApiTestTable.cs | 8 +++----- 5 files changed, 5 insertions(+), 29 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 5eb7d7cdeb..cb0f108366 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -2863,11 +2863,11 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met return (T)(object)data.DateTime; } #if NET6_0_OR_GREATER - else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005) + else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { return (T)(object)data.DateOnly; } - else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005) + else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { return (T)(object)data.TimeOnly; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 5f155ea313..6e92b4f479 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -3160,9 +3160,6 @@ public Customer(int id, string firstName, string lastName) Id = id; FirstName = firstName; LastName = lastName; -#if NET6_0_OR_GREATER - DateOfBirth = new DateOnly(1990, 1, 31); -#endif } public int Id { get; set; } @@ -3170,10 +3167,6 @@ public Customer(int id, string firstName, string lastName) public string FirstName { get; set; } public string LastName { get; set; } - -#if NET6_0_OR_GREATER - public DateOnly DateOfBirth { get; set; } -#endif } internal class TestAsyncCallBackStateObject diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs index 99981f190b..874c225d9f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs @@ -149,9 +149,6 @@ public IEnumerator GetEnumerator() yield return new object[] { connStrAE, @"select CustomerId, FirstName, LastName from [{0}] ", 3, new string[] { @"int", @"string", @"string" } }; yield return new object[] { connStrAE, @"select CustomerId, FirstName from [{0}] ", 2, new string[] { @"int", @"string" } }; yield return new object[] { connStrAE, @"select LastName from [{0}] ", 1, new string[] { @"string" }}; -#if NET6_0_OR_GREATER - yield return new object[] { connStrAE, @"select DateOfBirth from [{0}] ", 1, new string[] { @"DateOnly" } }; -#endif } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index bc97da66bf..1ebdcfe04d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -20,11 +20,7 @@ class DatabaseHelper internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransaction transaction, string tableName, Customer customer) { using SqlCommand sqlCommand = new( -#if NET6_0_OR_GREATER - $"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName, DateOfBirth) VALUES (@CustomerId, @FirstName, @LastName, @DateOfBirth);", -#else $"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName) VALUES (@CustomerId, @FirstName, @LastName);", -#endif connection: sqlConnection, transaction: transaction, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled); @@ -32,9 +28,6 @@ internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransact sqlCommand.Parameters.AddWithValue(@"CustomerId", customer.Id); sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName); sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName); -#if NET6_0_OR_GREATER - sqlCommand.Parameters.AddWithValue(@"DateOfBirth", customer.DateOfBirth); -#endif sqlCommand.ExecuteNonQuery(); } @@ -163,11 +156,6 @@ public static void CompareResults(SqlDataReader sqlDataReader, string[] paramete Assert.True(sqlDataReader.GetInt32(columnsRead) == 45, "FAILED: read int value does not match."); break; -#if NET6_0_OR_GREATER - case "DateOnly": - Assert.True(sqlDataReader.GetFieldValue(columnsRead) == new DateOnly(1990, 1, 31), "FAILED: read DateOnly value does not match."); - break; -#endif default: Assert.Fail("FAILED: unexpected data type."); break; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs index a4568dd407..8ab0421707 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs @@ -26,14 +26,12 @@ public override void Create(SqlConnection sqlConnection) ( [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), - [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}')"; + [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}') + )"; -#if NET6_0_OR_GREATER - sql = sql + $",[DateOfBirth] [date] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}')"; -#endif using (SqlCommand command = sqlConnection.CreateCommand()) { - command.CommandText = sql + ")"; + command.CommandText = sql; command.ExecuteNonQuery(); } } From 1bd57b665a7dde6b30f5130b748d966c8cd12979 Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 18 Dec 2023 13:38:32 +0100 Subject: [PATCH 3/8] Isolate tests fixes #2274 --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 4 +- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 4 + .../AlwaysEncrypted/DateOnlyReadTests.cs | 90 +++++++++++++++++++ .../TestFixtures/DatabaseHelper.cs | 26 +++++- .../TestFixtures/SQLSetupStrategy.cs | 8 +- .../TestFixtures/Setup/DateOnlyTestTable.cs | 43 +++++++++ ....Data.SqlClient.ManualTesting.Tests.csproj | 34 +++---- 7 files changed, 183 insertions(+), 26 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs index cb0f108366..5eb7d7cdeb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -2863,11 +2863,11 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met return (T)(object)data.DateTime; } #if NET6_0_OR_GREATER - else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) + else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005) { return (T)(object)data.DateOnly; } - else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) + else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005) { return (T)(object)data.TimeOnly; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 6e92b4f479..9bfddd05d9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -3169,6 +3169,10 @@ public Customer(int id, string firstName, string lastName) public string LastName { get; set; } } +#if NET6_0_OR_GREATER + public record CustomerDateOnly(int Id, string FirstName, string LastName, DateOnly DateOfBirth); +#endif + internal class TestAsyncCallBackStateObject { /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs new file mode 100644 index 0000000000..30538d66b9 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted +{ + public sealed class DateOnlyReadTests : IClassFixture, IDisposable + { + private SQLSetupStrategy fixture; + + private readonly string tableName; + + public DateOnlyReadTests(PlatformSpecificTestContext context) + { + fixture = context.Fixture; + tableName = fixture.DateOnlyTestTable.Name; + } + + // tests + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] + [ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsData))] + public void TestSelectOnEncryptedNonEncryptedColumns(string connString, string selectQuery, int totalColumnsInSelect, string[] types) + { + Assert.False(string.IsNullOrWhiteSpace(selectQuery), "FAILED: select query should not be null or empty."); + Assert.True(totalColumnsInSelect <= 3, "FAILED: totalColumnsInSelect should <= 3."); + + using (SqlConnection sqlConn = new SqlConnection(connString)) + { + sqlConn.Open(); + + Table.DeleteData(tableName, sqlConn); + + // insert 1 row data + CustomerDateOnly customer = new CustomerDateOnly(45, "Microsoft", "Corporation", new DateOnly(2001, 1, 31)); + + DatabaseHelper.InsertCustomerDateOnlyData(sqlConn, null, tableName, customer); + + using (SqlCommand sqlCommand = new SqlCommand(string.Format(selectQuery, tableName), + sqlConn, null, SqlCommandColumnEncryptionSetting.Enabled)) + { + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) + { + Assert.True(sqlDataReader.HasRows, "FAILED: Select statement did not return any rows."); + + while (sqlDataReader.Read()) + { + DatabaseHelper.CompareResults(sqlDataReader, types, totalColumnsInSelect); + } + } + } + } + } + + + public void Dispose() + { + foreach (string connStrAE in DataTestUtility.AEConnStringsSetup) + { + using (SqlConnection sqlConnection = new SqlConnection(connStrAE)) + { + sqlConnection.Open(); + Table.DeleteData(fixture.DateOnlyTestTable.Name, sqlConnection); + } + } + } + } + + public class TestSelectOnEncryptedNonEncryptedColumnsDataDateOnly : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, @"select CustomerId, FirstName, LastName from [{0}] ", 3, new string[] { @"int", @"string", @"string" } }; + yield return new object[] { connStrAE, @"select CustomerId, FirstName from [{0}] ", 2, new string[] { @"int", @"string" } }; + yield return new object[] { connStrAE, @"select LastName from [{0}] ", 1, new string[] { @"string" }}; + yield return new object[] { connStrAE, @"select DateOfBirth from [{0}] ", 1, new string[] { @"DateOnly" } }; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} +#endif diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index 1ebdcfe04d..5d32310bca 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -31,6 +31,26 @@ internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransact sqlCommand.ExecuteNonQuery(); } +#if NET6_0_OR_GREATER + /// + /// Insert CustomerDateOnly record into table + /// + internal static void InsertCustomerDateOnlyData(SqlConnection sqlConnection, SqlTransaction transaction, string tableName, CustomerDateOnly customer) + { + using SqlCommand sqlCommand = new( + $"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName, DateOfBirth) VALUES (@CustomerId, @FirstName, @LastName, @DateOfBirth);", + connection: sqlConnection, + transaction: transaction, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled); + + sqlCommand.Parameters.AddWithValue(@"CustomerId", customer.Id); + sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName); + sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName); + sqlCommand.Parameters.AddWithValue(@"DateOfBirth", customer.DateOfBirth); + sqlCommand.ExecuteNonQuery(); + } +#endif + /// /// Validates that the results are the ones expected. /// @@ -155,7 +175,11 @@ public static void CompareResults(SqlDataReader sqlDataReader, string[] paramete case "int": Assert.True(sqlDataReader.GetInt32(columnsRead) == 45, "FAILED: read int value does not match."); break; - +#if NET6_0_OR_GREATER + case "DateOnly": + Assert.True(sqlDataReader.GetFieldValue(columnsRead) == new DateOnly(2001, 1, 31), "FAILED: read DateOnly value does not match."); + break; +#endif default: Assert.Fail("FAILED: unexpected data type."); break; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs index 0cd8436cb4..35b14643ec 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -21,6 +21,9 @@ public class SQLSetupStrategy : IDisposable public Table BulkCopyAEErrorMessageTestTable { get; private set; } public Table BulkCopyAETestTable { get; private set; } public Table SqlParameterPropertiesTable { get; private set; } +#if NET6_0_OR_GREATER + public Table DateOnlyTestTable { get; private set; } +#endif public Table End2EndSmokeTable { get; private set; } public Table TrustedMasterKeyPathsTestTable { get; private set; } public Table SqlNullValuesTable { get; private set; } @@ -130,7 +133,10 @@ protected List CreateTables(IList columnEncryptionKe End2EndSmokeTable = new ApiTestTable(GenerateUniqueName("End2EndSmokeTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); tables.Add(End2EndSmokeTable); - +#if NET6_0_OR_GREATER + DateOnlyTestTable = new DateOnlyTestTable(GenerateUniqueName("DateOnlyTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); + tables.Add(DateOnlyTestTable); +#endif TrustedMasterKeyPathsTestTable = new ApiTestTable(GenerateUniqueName("TrustedMasterKeyPathsTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); tables.Add(TrustedMasterKeyPathsTestTable); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs new file mode 100644 index 0000000000..22ef7bbaef --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup +{ + public class DateOnlyTestTable : Table + { + private const string ColumnEncryptionAlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA_256"; + public ColumnEncryptionKey columnEncryptionKey1; + public ColumnEncryptionKey columnEncryptionKey2; + private bool useDeterministicEncryption; + + public DateOnlyTestTable(string tableName, ColumnEncryptionKey columnEncryptionKey1, ColumnEncryptionKey columnEncryptionKey2, bool useDeterministicEncryption = false) : base(tableName) + { + this.columnEncryptionKey1 = columnEncryptionKey1; + this.columnEncryptionKey2 = columnEncryptionKey2; + this.useDeterministicEncryption = useDeterministicEncryption; + } + + public override void Create(SqlConnection sqlConnection) + { + string encryptionType = useDeterministicEncryption ? "DETERMINISTIC" : DataTestUtility.EnclaveEnabled ? "RANDOMIZED" : "DETERMINISTIC"; + string sql = + $@"CREATE TABLE [dbo].[{Name}] + ( + [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [DateOfBirth] [date] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}') + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + } +} +#endif diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 99cc3268ca..f535652547 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -260,6 +260,8 @@ + + @@ -298,30 +300,19 @@ - - - - - - - + + + + + + + - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -334,8 +325,7 @@ all - + From 55244025a02be460c41525ae9c091f3def8628cb Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 18 Dec 2023 14:08:20 +0100 Subject: [PATCH 4/8] fix build --- .../tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs index 30538d66b9..9ffa330e62 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs @@ -25,7 +25,7 @@ public DateOnlyReadTests(PlatformSpecificTestContext context) // tests [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] - [ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsData))] + [ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsDataDateOnly))] public void TestSelectOnEncryptedNonEncryptedColumns(string connString, string selectQuery, int totalColumnsInSelect, string[] types) { Assert.False(string.IsNullOrWhiteSpace(selectQuery), "FAILED: select query should not be null or empty."); From e1773f1985bac9c483dae4268c5765bcd74cf294 Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 18 Dec 2023 14:16:38 +0100 Subject: [PATCH 5/8] fix test build? --- .../tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs | 2 -- .../AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs | 2 -- .../Microsoft.Data.SqlClient.ManualTesting.Tests.csproj | 4 ++-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs index 9ffa330e62..b3ca935df4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if NET6_0_OR_GREATER using System; using System.Collections; using System.Collections.Generic; @@ -87,4 +86,3 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } -#endif diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs index 22ef7bbaef..8144875a1a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if NET6_0_OR_GREATER namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup { public class DateOnlyTestTable : Table @@ -40,4 +39,3 @@ [DateOfBirth] [date] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionK } } -#endif diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index f535652547..d72cc692ce 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -251,6 +251,8 @@ + + @@ -260,8 +262,6 @@ - - From c612a133fed3fee2ca0936ce48ed9a34885350d1 Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 18 Dec 2023 14:30:40 +0100 Subject: [PATCH 6/8] Enable test build? --- .../AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs index 35b14643ec..4d3c635684 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -1,4 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. + +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information.using System; @@ -21,9 +22,7 @@ public class SQLSetupStrategy : IDisposable public Table BulkCopyAEErrorMessageTestTable { get; private set; } public Table BulkCopyAETestTable { get; private set; } public Table SqlParameterPropertiesTable { get; private set; } -#if NET6_0_OR_GREATER public Table DateOnlyTestTable { get; private set; } -#endif public Table End2EndSmokeTable { get; private set; } public Table TrustedMasterKeyPathsTestTable { get; private set; } public Table SqlNullValuesTable { get; private set; } @@ -133,10 +132,10 @@ protected List
CreateTables(IList columnEncryptionKe End2EndSmokeTable = new ApiTestTable(GenerateUniqueName("End2EndSmokeTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); tables.Add(End2EndSmokeTable); -#if NET6_0_OR_GREATER + DateOnlyTestTable = new DateOnlyTestTable(GenerateUniqueName("DateOnlyTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); tables.Add(DateOnlyTestTable); -#endif + TrustedMasterKeyPathsTestTable = new ApiTestTable(GenerateUniqueName("TrustedMasterKeyPathsTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); tables.Add(TrustedMasterKeyPathsTestTable); From 00d590fa35698a2bc8554afec893c3956b98f0fe Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 18 Dec 2023 14:46:38 +0100 Subject: [PATCH 7/8] Maybe this time?? --- .../Microsoft.Data.SqlClient.ManualTesting.Tests.csproj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index d72cc692ce..52223a7bb4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -56,6 +56,7 @@ + @@ -69,6 +70,9 @@ + + + @@ -251,8 +255,6 @@ - - From 6bfcac201f50d50413a68f52f660616e7cafc79b Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 18 Dec 2023 17:04:04 +0100 Subject: [PATCH 8/8] Add "time" test --- .../tests/ManualTests/AlwaysEncrypted/ApiShould.cs | 2 +- .../ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs | 9 +++++++-- .../AlwaysEncrypted/TestFixtures/DatabaseHelper.cs | 7 ++++++- .../TestFixtures/Setup/DateOnlyTestTable.cs | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 9bfddd05d9..f6e203c8b7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -3170,7 +3170,7 @@ public Customer(int id, string firstName, string lastName) } #if NET6_0_OR_GREATER - public record CustomerDateOnly(int Id, string FirstName, string LastName, DateOnly DateOfBirth); + public record CustomerDateOnly(int Id, string FirstName, string LastName, DateOnly DateOfBirth, TimeOnly TimeOfDay); #endif internal class TestAsyncCallBackStateObject diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs index b3ca935df4..a211fe6343 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs @@ -37,7 +37,12 @@ public void TestSelectOnEncryptedNonEncryptedColumns(string connString, string s Table.DeleteData(tableName, sqlConn); // insert 1 row data - CustomerDateOnly customer = new CustomerDateOnly(45, "Microsoft", "Corporation", new DateOnly(2001, 1, 31)); + CustomerDateOnly customer = new CustomerDateOnly( + 45, + "Microsoft", + "Corporation", + new DateOnly(2001, 1, 31), + new TimeOnly(18, 36, 45)); DatabaseHelper.InsertCustomerDateOnlyData(sqlConn, null, tableName, customer); @@ -80,7 +85,7 @@ public IEnumerator GetEnumerator() yield return new object[] { connStrAE, @"select CustomerId, FirstName, LastName from [{0}] ", 3, new string[] { @"int", @"string", @"string" } }; yield return new object[] { connStrAE, @"select CustomerId, FirstName from [{0}] ", 2, new string[] { @"int", @"string" } }; yield return new object[] { connStrAE, @"select LastName from [{0}] ", 1, new string[] { @"string" }}; - yield return new object[] { connStrAE, @"select DateOfBirth from [{0}] ", 1, new string[] { @"DateOnly" } }; + yield return new object[] { connStrAE, @"select DateOfBirth, TimeOfDay from [{0}] ", 2, new string[] { @"DateOnly", "TimeOnly" } }; } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index 5d32310bca..459573606e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -38,7 +38,7 @@ internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransact internal static void InsertCustomerDateOnlyData(SqlConnection sqlConnection, SqlTransaction transaction, string tableName, CustomerDateOnly customer) { using SqlCommand sqlCommand = new( - $"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName, DateOfBirth) VALUES (@CustomerId, @FirstName, @LastName, @DateOfBirth);", + $"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName, DateOfBirth, TimeOfDay) VALUES (@CustomerId, @FirstName, @LastName, @DateOfBirth, @TimeOfDay);", connection: sqlConnection, transaction: transaction, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled); @@ -47,6 +47,7 @@ internal static void InsertCustomerDateOnlyData(SqlConnection sqlConnection, Sql sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName); sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName); sqlCommand.Parameters.AddWithValue(@"DateOfBirth", customer.DateOfBirth); + sqlCommand.Parameters.AddWithValue(@"TimeOfDay", customer.TimeOfDay); sqlCommand.ExecuteNonQuery(); } #endif @@ -179,6 +180,10 @@ public static void CompareResults(SqlDataReader sqlDataReader, string[] paramete case "DateOnly": Assert.True(sqlDataReader.GetFieldValue(columnsRead) == new DateOnly(2001, 1, 31), "FAILED: read DateOnly value does not match."); break; + + case "TimeOnly": + Assert.True(sqlDataReader.GetFieldValue(columnsRead) == new TimeOnly(18, 36, 45), "FAILED: read TimeOnly value does not match."); + break; #endif default: Assert.Fail("FAILED: unexpected data type."); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs index 8144875a1a..6cc9bb6f66 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs @@ -27,6 +27,7 @@ public override void Create(SqlConnection sqlConnection) [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [TimeOfDay] [time] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), [DateOfBirth] [date] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}') )";