Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix | Enable reading AE date as DateOnly #2275

Merged
merged 8 commits into from
Jan 12, 2024
Merged
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 @@ -2863,11 +2863,11 @@ private T GetFieldValueFromSqlBufferInternal<T>(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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, TimeOnly TimeOfDay);
#endif

internal class TestAsyncCallBackStateObject
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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;
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<PlatformSpecificTestContext>, 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(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.");
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),
new TimeOnly(18, 36, 45));

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<object[]>
{
public IEnumerator<object[]> 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, TimeOfDay from [{0}] ", 2, new string[] { @"DateOnly", "TimeOnly" } };
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransact
sqlCommand.ExecuteNonQuery();
}

#if NET6_0_OR_GREATER
/// <summary>
/// Insert CustomerDateOnly record into table
/// </summary>
internal static void InsertCustomerDateOnlyData(SqlConnection sqlConnection, SqlTransaction transaction, string tableName, CustomerDateOnly customer)
{
using SqlCommand sqlCommand = new(
$"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName, DateOfBirth, TimeOfDay) VALUES (@CustomerId, @FirstName, @LastName, @DateOfBirth, @TimeOfDay);",
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.Parameters.AddWithValue(@"TimeOfDay", customer.TimeOfDay);
sqlCommand.ExecuteNonQuery();
}
#endif

/// <summary>
/// Validates that the results are the ones expected.
/// </summary>
Expand Down Expand Up @@ -155,7 +176,15 @@ 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<DateOnly>(columnsRead) == new DateOnly(2001, 1, 31), "FAILED: read DateOnly value does not match.");
break;

case "TimeOnly":
Assert.True(sqlDataReader.GetFieldValue<TimeOnly>(columnsRead) == new TimeOnly(18, 36, 45), "FAILED: read TimeOnly value does not match.");
break;
#endif
default:
Assert.Fail("FAILED: unexpected data type.");
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -21,6 +22,7 @@ public class SQLSetupStrategy : IDisposable
public Table BulkCopyAEErrorMessageTestTable { get; private set; }
public Table BulkCopyAETestTable { get; private set; }
public Table SqlParameterPropertiesTable { get; private set; }
public Table DateOnlyTestTable { get; private set; }
public Table End2EndSmokeTable { get; private set; }
public Table TrustedMasterKeyPathsTestTable { get; private set; }
public Table SqlNullValuesTable { get; private set; }
Expand Down Expand Up @@ -131,6 +133,9 @@ protected List<Table> CreateTables(IList<ColumnEncryptionKey> columnEncryptionKe
End2EndSmokeTable = new ApiTestTable(GenerateUniqueName("End2EndSmokeTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]);
tables.Add(End2EndSmokeTable);

DateOnlyTestTable = new DateOnlyTestTable(GenerateUniqueName("DateOnlyTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]);
tables.Add(DateOnlyTestTable);

TrustedMasterKeyPathsTestTable = new ApiTestTable(GenerateUniqueName("TrustedMasterKeyPathsTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]);
tables.Add(TrustedMasterKeyPathsTestTable);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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.

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}'),
[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}')
)";

using (SqlCommand command = sqlConnection.CreateCommand())
{
command.CommandText = sql;
command.ExecuteNonQuery();
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\BulkCopyAETestTable.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\BulkCopyAEErrorMessageTestTable.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\BulkCopyTruncationTables.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\DateOnlyTestTable.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\SqlNullValuesTable.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\SqlParameterPropertiesTable.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\DbObject.cs" />
Expand All @@ -69,6 +70,9 @@
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\DummyProviderMasterKey.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\CertificateUtility.cs" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0')) AND ('$(TestSet)' == '' OR '$(TestSet)' == 'AE')">
<Compile Include="AlwaysEncrypted\DateOnlyReadTests.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TestSet)' == '' OR '$(TestSet)' == '1'">
<Compile Include="SQL\AsyncTest\AsyncTimeoutTest.cs" />
<Compile Include="SQL\AsyncTest\BeginExecAsyncTest.cs" />
Expand Down Expand Up @@ -298,30 +302,19 @@
<ProjectReference Include="$(TestsPath)tools\TDS\TDS.EndPoint\TDS.EndPoint.csproj" />
<ProjectReference Include="$(TestsPath)tools\TDS\TDS.Servers\TDS.Servers.csproj" />
<ProjectReference Include="$(TestsPath)tools\TDS\TDS\TDS.csproj" />
<ProjectReference
Include="$(TestsPath)tools\Microsoft.Data.SqlClient.TestUtilities\Microsoft.Data.SqlClient.TestUtilities.csproj" />
<ProjectReference Condition="'$(TargetGroup)'=='netcoreapp' AND $(ReferenceType)=='Project'"
Include="$(NetCoreSource)src\Microsoft.Data.SqlClient.csproj" />
<ProjectReference Condition="'$(TargetGroup)'=='netfx' AND $(ReferenceType)=='Project'"
Include="$(NetFxSource)src\Microsoft.Data.SqlClient.csproj" />
<ProjectReference Condition="$(ReferenceType.Contains('NetStandard'))"
Include="$(TestsPath)NSLibrary\Microsoft.Data.SqlClient.NSLibrary.csproj" />
<ProjectReference Condition="!$(ReferenceType.Contains('Package'))"
Include="$(SqlServerSource)Microsoft.SqlServer.Server.csproj" />
<PackageReference Condition="$(ReferenceType.Contains('Package'))"
Include="Microsoft.Data.SqlClient" Version="$(TestMicrosoftDataSqlClientVersion)" />
<ProjectReference
Include="$(TestsPath)CustomConfigurableRetryLogic\CustomRetryLogicProvider.csproj" />
<ProjectReference Include="$(TestsPath)tools\Microsoft.Data.SqlClient.TestUtilities\Microsoft.Data.SqlClient.TestUtilities.csproj" />
<ProjectReference Condition="'$(TargetGroup)'=='netcoreapp' AND $(ReferenceType)=='Project'" Include="$(NetCoreSource)src\Microsoft.Data.SqlClient.csproj" />
<ProjectReference Condition="'$(TargetGroup)'=='netfx' AND $(ReferenceType)=='Project'" Include="$(NetFxSource)src\Microsoft.Data.SqlClient.csproj" />
<ProjectReference Condition="$(ReferenceType.Contains('NetStandard'))" Include="$(TestsPath)NSLibrary\Microsoft.Data.SqlClient.NSLibrary.csproj" />
<ProjectReference Condition="!$(ReferenceType.Contains('Package'))" Include="$(SqlServerSource)Microsoft.SqlServer.Server.csproj" />
<PackageReference Condition="$(ReferenceType.Contains('Package'))" Include="Microsoft.Data.SqlClient" Version="$(TestMicrosoftDataSqlClientVersion)" />
<ProjectReference Include="$(TestsPath)CustomConfigurableRetryLogic\CustomRetryLogicProvider.csproj" />
</ItemGroup>
<!-- XUnit and XUnit extensions -->
<ItemGroup>
<PackageReference Condition="$(TargetGroup) == 'netfx'"
Include="System.Runtime.InteropServices.RuntimeInformation"
Version="$(SystemRuntimeInteropServicesRuntimeInformationVersion)" />
<PackageReference Condition="$(TargetGroup) == 'netfx'" Include="System.Runtime.InteropServices.RuntimeInformation" Version="$(SystemRuntimeInteropServicesRuntimeInformationVersion)" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies"
Version="$(MicrosoftNETFrameworkReferenceAssembliesVersion)"
Condition="'$(TargetGroup)' == 'netfx'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="$(MicrosoftNETFrameworkReferenceAssembliesVersion)" Condition="'$(TargetGroup)' == 'netfx'">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand All @@ -334,8 +327,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="xunit.runner.utility" Version="$(XunitVersion)" />
<PackageReference Include="Microsoft.DotNet.XUnitExtensions"
Version="$(MicrosoftDotNetXUnitExtensionsVersion)" />
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" Version="$(MicrosoftDotNetXUnitExtensionsVersion)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
Expand Down
Loading