Skip to content

Commit 01a589e

Browse files
author
Javad
authored
[5.1.5] Fix | Enable reading AE date as DateOnly (#2275) (#2324)
1 parent 759dc69 commit 01a589e

File tree

8 files changed

+180
-4
lines changed

8 files changed

+180
-4
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2844,11 +2844,11 @@ private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData met
28442844
return (T)(object)data.DateTime;
28452845
}
28462846
#if NET6_0_OR_GREATER
2847-
else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType)
2847+
else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005)
28482848
{
28492849
return (T)(object)data.DateOnly;
28502850
}
2851-
else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType)
2851+
else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005)
28522852
{
28532853
return (T)(object)data.TimeOnly;
28542854
}

src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs

+4
Original file line numberDiff line numberDiff line change
@@ -3169,6 +3169,10 @@ public Customer(int id, string firstName, string lastName)
31693169
public string LastName { get; set; }
31703170
}
31713171

3172+
#if NET6_0_OR_GREATER
3173+
public record CustomerDateOnly(int Id, string FirstName, string LastName, DateOnly DateOfBirth, TimeOnly TimeOfDay);
3174+
#endif
3175+
31723176
internal class TestAsyncCallBackStateObject
31733177
{
31743178
/// <summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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;
6+
using System.Collections;
7+
using System.Collections.Generic;
8+
using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup;
9+
using Xunit;
10+
11+
namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted
12+
{
13+
public sealed class DateOnlyReadTests : IClassFixture<PlatformSpecificTestContext>, IDisposable
14+
{
15+
private SQLSetupStrategy fixture;
16+
17+
private readonly string tableName;
18+
19+
public DateOnlyReadTests(PlatformSpecificTestContext context)
20+
{
21+
fixture = context.Fixture;
22+
tableName = fixture.DateOnlyTestTable.Name;
23+
}
24+
25+
// tests
26+
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))]
27+
[ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsDataDateOnly))]
28+
public void TestSelectOnEncryptedNonEncryptedColumns(string connString, string selectQuery, int totalColumnsInSelect, string[] types)
29+
{
30+
Assert.False(string.IsNullOrWhiteSpace(selectQuery), "FAILED: select query should not be null or empty.");
31+
Assert.True(totalColumnsInSelect <= 3, "FAILED: totalColumnsInSelect should <= 3.");
32+
33+
using (SqlConnection sqlConn = new SqlConnection(connString))
34+
{
35+
sqlConn.Open();
36+
37+
Table.DeleteData(tableName, sqlConn);
38+
39+
// insert 1 row data
40+
CustomerDateOnly customer = new CustomerDateOnly(
41+
45,
42+
"Microsoft",
43+
"Corporation",
44+
new DateOnly(2001, 1, 31),
45+
new TimeOnly(18, 36, 45));
46+
47+
DatabaseHelper.InsertCustomerDateOnlyData(sqlConn, null, tableName, customer);
48+
49+
using (SqlCommand sqlCommand = new SqlCommand(string.Format(selectQuery, tableName),
50+
sqlConn, null, SqlCommandColumnEncryptionSetting.Enabled))
51+
{
52+
using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader())
53+
{
54+
Assert.True(sqlDataReader.HasRows, "FAILED: Select statement did not return any rows.");
55+
56+
while (sqlDataReader.Read())
57+
{
58+
DatabaseHelper.CompareResults(sqlDataReader, types, totalColumnsInSelect);
59+
}
60+
}
61+
}
62+
}
63+
}
64+
65+
66+
public void Dispose()
67+
{
68+
foreach (string connStrAE in DataTestUtility.AEConnStringsSetup)
69+
{
70+
using (SqlConnection sqlConnection = new SqlConnection(connStrAE))
71+
{
72+
sqlConnection.Open();
73+
Table.DeleteData(fixture.DateOnlyTestTable.Name, sqlConnection);
74+
}
75+
}
76+
}
77+
}
78+
79+
public class TestSelectOnEncryptedNonEncryptedColumnsDataDateOnly : IEnumerable<object[]>
80+
{
81+
public IEnumerator<object[]> GetEnumerator()
82+
{
83+
foreach (string connStrAE in DataTestUtility.AEConnStrings)
84+
{
85+
yield return new object[] { connStrAE, @"select CustomerId, FirstName, LastName from [{0}] ", 3, new string[] { @"int", @"string", @"string" } };
86+
yield return new object[] { connStrAE, @"select CustomerId, FirstName from [{0}] ", 2, new string[] { @"int", @"string" } };
87+
yield return new object[] { connStrAE, @"select LastName from [{0}] ", 1, new string[] { @"string" }};
88+
yield return new object[] { connStrAE, @"select DateOfBirth, TimeOfDay from [{0}] ", 2, new string[] { @"DateOnly", "TimeOnly" } };
89+
}
90+
}
91+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
92+
}
93+
}

src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs

+29
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransact
3131
sqlCommand.ExecuteNonQuery();
3232
}
3333

34+
#if NET6_0_OR_GREATER
35+
/// <summary>
36+
/// Insert CustomerDateOnly record into table
37+
/// </summary>
38+
internal static void InsertCustomerDateOnlyData(SqlConnection sqlConnection, SqlTransaction transaction, string tableName, CustomerDateOnly customer)
39+
{
40+
using SqlCommand sqlCommand = new(
41+
$"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName, DateOfBirth, TimeOfDay) VALUES (@CustomerId, @FirstName, @LastName, @DateOfBirth, @TimeOfDay);",
42+
connection: sqlConnection,
43+
transaction: transaction,
44+
columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled);
45+
46+
sqlCommand.Parameters.AddWithValue(@"CustomerId", customer.Id);
47+
sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName);
48+
sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName);
49+
sqlCommand.Parameters.AddWithValue(@"DateOfBirth", customer.DateOfBirth);
50+
sqlCommand.Parameters.AddWithValue(@"TimeOfDay", customer.TimeOfDay);
51+
sqlCommand.ExecuteNonQuery();
52+
}
53+
#endif
54+
3455
/// <summary>
3556
/// Validates that the results are the ones expected.
3657
/// </summary>
@@ -155,7 +176,15 @@ public static void CompareResults(SqlDataReader sqlDataReader, string[] paramete
155176
case "int":
156177
Assert.True(sqlDataReader.GetInt32(columnsRead) == 45, "FAILED: read int value does not match.");
157178
break;
179+
#if NET6_0_OR_GREATER
180+
case "DateOnly":
181+
Assert.True(sqlDataReader.GetFieldValue<DateOnly>(columnsRead) == new DateOnly(2001, 1, 31), "FAILED: read DateOnly value does not match.");
182+
break;
158183

184+
case "TimeOnly":
185+
Assert.True(sqlDataReader.GetFieldValue<TimeOnly>(columnsRead) == new TimeOnly(18, 36, 45), "FAILED: read TimeOnly value does not match.");
186+
break;
187+
#endif
159188
default:
160189
Assert.True(false, "FAILED: unexpected data type.");
161190
break;

src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+

2+
// Licensed to the .NET Foundation under one or more agreements.
23
// The .NET Foundation licenses this file to you under the MIT license.
34
// See the LICENSE file in the project root for more information.using System;
45

@@ -21,6 +22,7 @@ public class SQLSetupStrategy : IDisposable
2122
public Table BulkCopyAEErrorMessageTestTable { get; private set; }
2223
public Table BulkCopyAETestTable { get; private set; }
2324
public Table SqlParameterPropertiesTable { get; private set; }
25+
public Table DateOnlyTestTable { get; private set; }
2426
public Table End2EndSmokeTable { get; private set; }
2527
public Table TrustedMasterKeyPathsTestTable { get; private set; }
2628
public Table SqlNullValuesTable { get; private set; }
@@ -131,6 +133,9 @@ protected List<Table> CreateTables(IList<ColumnEncryptionKey> columnEncryptionKe
131133
End2EndSmokeTable = new ApiTestTable(GenerateUniqueName("End2EndSmokeTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]);
132134
tables.Add(End2EndSmokeTable);
133135

136+
DateOnlyTestTable = new DateOnlyTestTable(GenerateUniqueName("DateOnlyTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]);
137+
tables.Add(DateOnlyTestTable);
138+
134139
TrustedMasterKeyPathsTestTable = new ApiTestTable(GenerateUniqueName("TrustedMasterKeyPathsTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]);
135140
tables.Add(TrustedMasterKeyPathsTestTable);
136141

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup
6+
{
7+
public class DateOnlyTestTable : Table
8+
{
9+
private const string ColumnEncryptionAlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA_256";
10+
public ColumnEncryptionKey columnEncryptionKey1;
11+
public ColumnEncryptionKey columnEncryptionKey2;
12+
private bool useDeterministicEncryption;
13+
14+
public DateOnlyTestTable(string tableName, ColumnEncryptionKey columnEncryptionKey1, ColumnEncryptionKey columnEncryptionKey2, bool useDeterministicEncryption = false) : base(tableName)
15+
{
16+
this.columnEncryptionKey1 = columnEncryptionKey1;
17+
this.columnEncryptionKey2 = columnEncryptionKey2;
18+
this.useDeterministicEncryption = useDeterministicEncryption;
19+
}
20+
21+
public override void Create(SqlConnection sqlConnection)
22+
{
23+
string encryptionType = useDeterministicEncryption ? "DETERMINISTIC" : DataTestUtility.EnclaveEnabled ? "RANDOMIZED" : "DETERMINISTIC";
24+
string sql =
25+
$@"CREATE TABLE [dbo].[{Name}]
26+
(
27+
[CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'),
28+
[FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'),
29+
[LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'),
30+
[TimeOfDay] [time] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}'),
31+
[DateOfBirth] [date] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}')
32+
)";
33+
34+
using (SqlCommand command = sqlConnection.CreateCommand())
35+
{
36+
command.CommandText = sql;
37+
command.ExecuteNonQuery();
38+
}
39+
}
40+
41+
}
42+
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ public static class DataTestUtility
6161
public static readonly bool IsDNSCachingSupportedTR = false; // this is for the tenant ring
6262
public static readonly string UserManagedIdentityClientId = null;
6363

64-
6564
public static readonly string EnclaveAzureDatabaseConnString = null;
6665
public static bool ManagedIdentitySupported = true;
6766
public static string AADAccessToken = null;

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

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\BulkCopyAETestTable.cs" />
5858
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\BulkCopyAEErrorMessageTestTable.cs" />
5959
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\BulkCopyTruncationTables.cs" />
60+
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\DateOnlyTestTable.cs" />
6061
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\SqlNullValuesTable.cs" />
6162
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\SqlParameterPropertiesTable.cs" />
6263
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\DbObject.cs" />
@@ -70,6 +71,9 @@
7071
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\DummyProviderMasterKey.cs" />
7172
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\CertificateUtility.cs" />
7273
</ItemGroup>
74+
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0')) AND ('$(TestSet)' == '' OR '$(TestSet)' == 'AE')">
75+
<Compile Include="AlwaysEncrypted\DateOnlyReadTests.cs" />
76+
</ItemGroup>
7377
<ItemGroup Condition="'$(TestSet)' == '' OR '$(TestSet)' == '1'">
7478
<Compile Include="SQL\AsyncTest\AsyncTimeoutTest.cs" />
7579
<Compile Include="SQL\AsyncTest\BeginExecAsyncTest.cs" />

0 commit comments

Comments
 (0)