From 6a95ad44a3e00eb694e599b66cc27948acc57083 Mon Sep 17 00:00:00 2001 From: Javad Date: Tue, 13 Apr 2021 13:27:47 -0700 Subject: [PATCH 01/87] Test Lab net5 (#1021) --- .editorconfig | 3 +++ .../ManualTests/AlwaysEncrypted/ApiShould.cs | 2 +- .../ManualTests/AlwaysEncrypted/BulkCopyAE.cs | 2 +- .../AlwaysEncrypted/ConversionTests.cs | 14 +++++----- .../EnclaveAzureDatabaseTests.cs | 2 +- .../AlwaysEncrypted/End2EndSmokeTests.cs | 2 +- .../AlwaysEncrypted/ExceptionsGenericError.cs | 2 +- .../AlwaysEncrypted/SqlNullValues.cs | 2 +- .../TestFixtures/SQLSetupStrategy.cs | 18 ++++++++++++- .../ManualTests/DataCommon/ProxyServer.cs | 6 ++--- .../ManualTests/SQL/Common/AsyncDebugScope.cs | 27 ++++++++++++++----- .../SQL/ConnectivityTests/ConnectivityTest.cs | 11 +++++++- .../SQL/RandomStressTest/RandomStressTest.cs | 11 +++++++- .../SQL/RandomStressTest/RandomizerPool.cs | 9 ++++++- .../DataConversionErrorMessageTest.cs | 2 +- .../SqlNotificationTest.cs | 2 +- 16 files changed, 87 insertions(+), 28 deletions(-) diff --git a/.editorconfig b/.editorconfig index f5c888e04a..7acdd43c13 100644 --- a/.editorconfig +++ b/.editorconfig @@ -163,3 +163,6 @@ end_of_line = crlf # Analyzers dotnet_code_quality.ca1802.api_surface = private, internal + +[*.cs] +dotnet_code_quality.CA2100.excluded_type_names_with_derived_types = Microsoft.Data.SqlClient.ManualTesting.Tests.* diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index f44462842f..30e24f8960 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -20,7 +20,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted /// /// Always Encrypted public API Manual tests. /// - public class ApiShould : IClassFixture, IDisposable + public sealed class ApiShould : IClassFixture, IDisposable { private SQLSetupStrategy _fixture; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs index dd2b2b2561..6a58935cff 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs @@ -12,7 +12,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted /// /// Always Encrypted public API Manual tests. /// - public class BulkCopyAE : IClassFixture, IDisposable + public sealed class BulkCopyAE : IClassFixture, IDisposable { private SQLSetupStrategy fixture; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs index 3746b9988c..01604f4e29 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { [PlatformSpecific(TestPlatforms.Windows)] - public class ConversionTests : IDisposable + public sealed class ConversionTests : IDisposable { private const string IdentityColumnName = "IdentityColumn"; @@ -33,7 +33,7 @@ public class ConversionTests : IDisposable private ColumnMasterKey columnMasterKey; private ColumnEncryptionKey columnEncryptionKey; private SqlColumnEncryptionCertificateStoreProvider certStoreProvider = new SqlColumnEncryptionCertificateStoreProvider(); - protected List databaseObjects = new List(); + private List _databaseObjects = new List(); private class ColumnMetaData { @@ -60,19 +60,19 @@ public ConversionTests() certificate = CertificateUtility.CreateCertificate(); } columnMasterKey = new CspColumnMasterKey(DatabaseHelper.GenerateUniqueName("CMK"), certificate.Thumbprint, certStoreProvider, DataTestUtility.EnclaveEnabled); - databaseObjects.Add(columnMasterKey); + _databaseObjects.Add(columnMasterKey); columnEncryptionKey = new ColumnEncryptionKey(DatabaseHelper.GenerateUniqueName("CEK"), columnMasterKey, certStoreProvider); - databaseObjects.Add(columnEncryptionKey); + _databaseObjects.Add(columnEncryptionKey); foreach (string connectionStr in DataTestUtility.AEConnStringsSetup) { using (SqlConnection sqlConnection = new SqlConnection(connectionStr)) { sqlConnection.Open(); - databaseObjects.ForEach(o => o.Create(sqlConnection)); + _databaseObjects.ForEach(o => o.Create(sqlConnection)); } } } @@ -1345,13 +1345,13 @@ private void SetParamSizeScalePrecision(ref SqlParameter param, ColumnMetaData c public void Dispose() { - databaseObjects.Reverse(); + _databaseObjects.Reverse(); foreach (string connectionStr in DataTestUtility.AEConnStringsSetup) { using (SqlConnection sqlConnection = new SqlConnection(connectionStr)) { sqlConnection.Open(); - databaseObjects.ForEach(o => o.Drop(sqlConnection)); + _databaseObjects.ForEach(o => o.Drop(sqlConnection)); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs index 0602bea357..c372e39bca 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { // This test class is for internal use only - public class EnclaveAzureDatabaseTests : IDisposable + public sealed class EnclaveAzureDatabaseTests : IDisposable { private ColumnMasterKey akvColumnMasterKey; private ColumnEncryptionKey akvColumnEncryptionKey; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs index 88fc0f6f66..71339d4b67 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { - public class End2EndSmokeTests : IClassFixture, IDisposable + public sealed class End2EndSmokeTests : IClassFixture, IDisposable { private SQLSetupStrategy fixture; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionsGenericError.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionsGenericError.cs index d0e3f1d3f3..b3c51d7fbd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionsGenericError.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionsGenericError.cs @@ -106,7 +106,7 @@ public void TestParamUnexpectedEncryptionMD(string connectionString) } } - public class ExceptionGenericErrorFixture : IDisposable + public sealed class ExceptionGenericErrorFixture : IDisposable { static public string encryptedTableName; static public string encryptedProcedureName; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs index caa174b3ce..2f1a1d8ed6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { - public class SqlNullValuesTests : IClassFixture, IDisposable + public sealed class SqlNullValuesTests : IClassFixture, IDisposable { private SQLSetupStrategy fixture; private readonly string tableName; 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 38e81a041c..289bfc7246 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -250,6 +250,12 @@ protected List CreateTables(IList columnEncryptionKe protected string GenerateUniqueName(string baseName) => string.Concat("AE-", baseName, "-", Guid.NewGuid().ToString()); public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { databaseObjects.Reverse(); foreach (string value in DataTestUtility.AEConnStringsSetup) @@ -285,9 +291,19 @@ public PlatformSpecificTestContext() public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + try { - akvFixture?.Dispose(); + if (disposing) + { + akvFixture?.Dispose(); + } } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ProxyServer.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ProxyServer.cs index 7fcc26d702..4d42fe7bd9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ProxyServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ProxyServer.cs @@ -16,7 +16,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests /// This is a simple network listener that redirects traffic /// It is used to simulate network delay /// - public class ProxyServer : IDisposable + public sealed class ProxyServer : IDisposable { private volatile bool _stopRequested; private StringBuilder _eventLog; @@ -65,12 +65,12 @@ internal StringBuilder EventLog /// /// Gets/Sets the listener /// - protected TcpListener ListenerSocket { get; set; } + private TcpListener ListenerSocket { get; set; } /// /// Gets/Sets the listener thread /// - protected Thread ListenerThread { get; set; } + private Thread ListenerThread { get; set; } /// /// Delay incoming diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/AsyncDebugScope.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/AsyncDebugScope.cs index 520081e435..fa74467cc3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/AsyncDebugScope.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/AsyncDebugScope.cs @@ -47,12 +47,21 @@ public int ForceAsyncWriteDelay public void Dispose() { - TdsParserStateObjectHelper.FailAsyncPends = false; - TdsParserStateObjectHelper.ForceAllPends = false; - TdsParserStateObjectHelper.ForcePendingReadsToWaitForUser = false; - TdsParserStateObjectHelper.ForceSyncOverAsyncAfterFirstPend = false; - TdsParserStateObjectHelper.SkipSendAttention = false; - CommandHelper.ForceAsyncWriteDelay = 0; + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + TdsParserStateObjectHelper.FailAsyncPends = false; + TdsParserStateObjectHelper.ForceAllPends = false; + TdsParserStateObjectHelper.ForcePendingReadsToWaitForUser = false; + TdsParserStateObjectHelper.ForceSyncOverAsyncAfterFirstPend = false; + TdsParserStateObjectHelper.SkipSendAttention = false; + CommandHelper.ForceAsyncWriteDelay = 0; + } } } @@ -77,6 +86,12 @@ public PendAsyncReadsScope(SqlDataReader reader, int? errorCode = null) } public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { if (_reader != null) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs index 85f4f94917..62633de096 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -185,7 +185,16 @@ public static void Stop() public void Dispose() { - _doneEvent.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _doneEvent.Dispose(); + } } public void SqlConnectionOpen() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomStressTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomStressTest.cs index 8eb8ec7684..c7bc44b872 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomStressTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomStressTest.cs @@ -366,7 +366,16 @@ private void RunTestIteration(SqlConnection con, SqlRandomizer rand, SqlRandomTa public void Dispose() { - _endEvent?.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _endEvent?.Dispose(); + } } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomizerPool.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomizerPool.cs index c90779cd75..251cf9a85f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomizerPool.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomizerPool.cs @@ -272,12 +272,19 @@ Randomizer.State[] IScope.GetStates() Randomizer IScope.Current { get { return Current; } } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + /// /// Disposes the scope and reverts the current thread scope to previous one. /// Note that the "last created scope" is not changed on Dispose, thus the scope instance /// itself can still be used to collect repro states. /// - public void Dispose() + protected virtual void Dispose(bool disposing) { if (_current != null) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs index f9d32952d0..4c3d594ad1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs @@ -15,7 +15,7 @@ internal enum ColumnsEnum _varChar3 = 1 } - public class InitialDatabase : IDisposable + public sealed class InitialDatabase : IDisposable { private string srcConstr { get; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs index 5cf1e99e93..11827aa15f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs @@ -8,7 +8,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { - public class SqlNotificationTest : IDisposable + public sealed class SqlNotificationTest : IDisposable { // Misc constants private const int CALLBACK_TIMEOUT = 5000; // milliseconds From c34e8a4de8085c2e5fef24c277852d0bfc5e5618 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 14 Apr 2021 10:59:26 -0700 Subject: [PATCH 02/87] Fix | Fix possible server connection leak if an exception occurs in pooling layer (#890) --- .../Data/ProviderBase/DbConnectionPool.cs | 7 + .../Data/ProviderBase/DbConnectionPool.cs | 7 + .../SqlConnectionBasicTests.cs | 33 ++++ .../tests/FunctionalTests/TestTdsServer.cs | 18 ++- .../tools/TDS/TDS.Servers/TDS.Servers.csproj | 2 + .../TDS.Servers/TransientFaultTDSServer.cs | 148 ++++++++++++++++++ .../TransientFaultTDSServerArguments.cs | 34 ++++ 7 files changed, 241 insertions(+), 8 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServerArguments.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index 6b83fdce3b..c6f5a39693 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -778,7 +778,14 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio CheckPoolBlockingPeriod(e); + // Close associated Parser if connection already established. + if (newObj?.IsConnectionAlive() == true) + { + newObj.Dispose(); + } + newObj = null; // set to null, so we do not return bad new object + // Failed to create instance _resError = e; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index b69d3bb122..8d04056add 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -906,7 +906,14 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio throw; } + // Close associated Parser if connection already established. + if (newObj?.IsConnectionAlive() == true) + { + newObj.Dispose(); + } + newObj = null; // set to null, so we do not return bad new object + // Failed to create instance _resError = e; diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index c21275ebde..e34a04a18e 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -3,9 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Data; using System.Data.Common; using System.Reflection; using System.Security; +using Microsoft.SqlServer.TDS.Servers; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -40,6 +42,37 @@ public void IntegratedAuthConnectionTest() } } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] + [PlatformSpecific(TestPlatforms.Windows)] + public void TransientFaultTest() + { + using (TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, 40613)) + { + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder() + { + DataSource = "localhost," + server.Port, + IntegratedSecurity = true + }; + + using (SqlConnection connection = new SqlConnection(builder.ConnectionString)) + { + try + { + connection.Open(); + Assert.Equal(ConnectionState.Open, connection.State); + } + catch (Exception e) + { + if (null != connection) + { + Assert.Equal(ConnectionState.Closed, connection.State); + } + Assert.False(true, e.Message); + } + } + } + } + [Fact] public void SqlConnectionDbProviderFactoryTest() { diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs index 4607a5dc93..3df270718f 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs @@ -12,18 +12,20 @@ namespace Microsoft.Data.SqlClient.Tests { internal class TestTdsServer : GenericTDSServer, IDisposable { + private const int DefaultConnectionTimeout = 5; + private TDSServerEndPoint _endpoint = null; - private SqlConnectionStringBuilder connectionStringBuilder; + private SqlConnectionStringBuilder _connectionStringBuilder; public TestTdsServer(TDSServerArguments args) : base(args) { } public TestTdsServer(QueryEngine engine, TDSServerArguments args) : base(args) { - this.Engine = engine; + Engine = engine; } - public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, [CallerMemberName] string methodName = "") + public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, [CallerMemberName] string methodName = "") { TDSServerArguments args = new TDSServerArguments() { @@ -32,7 +34,7 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool if (enableFedAuth) { - args.FedAuthRequiredPreLoginOption = Microsoft.SqlServer.TDS.PreLogin.TdsPreLoginFedAuthRequiredOption.FedAuthRequired; + args.FedAuthRequiredPreLoginOption = SqlServer.TDS.PreLogin.TdsPreLoginFedAuthRequiredOption.FedAuthRequired; } TestTdsServer server = engine == null ? new TestTdsServer(args) : new TestTdsServer(engine, args); @@ -43,14 +45,14 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool server._endpoint.Start(); int port = server._endpoint.ServerEndPoint.Port; - server.connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = 5, Encrypt = false }; - server.ConnectionString = server.connectionStringBuilder.ConnectionString; + server._connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = false }; + server.ConnectionString = server._connectionStringBuilder.ConnectionString; return server; } - public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, [CallerMemberName] string methodName = "") + public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, [CallerMemberName] string methodName = "") { - return StartServerWithQueryEngine(null, false, false, methodName); + return StartServerWithQueryEngine(null, enableFedAuth, enableLog, connectionTimeout, methodName); } public void Dispose() => _endpoint?.Stop(); diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDS.Servers.csproj b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDS.Servers.csproj index c4ec16c2cc..03389cb3df 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDS.Servers.csproj +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDS.Servers.csproj @@ -25,6 +25,8 @@ + + Always diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs new file mode 100644 index 0000000000..9bec7fdb34 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs @@ -0,0 +1,148 @@ +// 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.Net; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.SqlServer.TDS.Done; +using Microsoft.SqlServer.TDS.EndPoint; +using Microsoft.SqlServer.TDS.Error; +using Microsoft.SqlServer.TDS.Login7; + +namespace Microsoft.SqlServer.TDS.Servers +{ + /// + /// TDS Server that authenticates clients according to the requested parameters + /// + public class TransientFaultTDSServer : GenericTDSServer, IDisposable + { + private static int RequestCounter = 0; + + public int Port { get; set; } + + /// + /// Constructor + /// + public TransientFaultTDSServer() => new TransientFaultTDSServer(new TransientFaultTDSServerArguments()); + + /// + /// Constructor + /// + /// + public TransientFaultTDSServer(TransientFaultTDSServerArguments arguments) : + base(arguments) + { } + + /// + /// Constructor + /// + /// + /// + public TransientFaultTDSServer(QueryEngine engine, TransientFaultTDSServerArguments args) : base(args) + { + Engine = engine; + } + + private TDSServerEndPoint _endpoint = null; + + private static string GetErrorMessage(uint errorNumber) + { + switch (errorNumber) + { + case 40613: + return "Database on server is not currently available. Please retry the connection later. " + + "If the problem persists, contact customer support, and provide them the session tracing ID."; + } + return "Unknown server error occurred"; + } + + /// + /// Handler for login request + /// + public override TDSMessageCollection OnLogin7Request(ITDSServerSession session, TDSMessage request) + { + // Inflate login7 request from the message + TDSLogin7Token loginRequest = request[0] as TDSLogin7Token; + + // Check if arguments are of the transient fault TDS server + if (Arguments is TransientFaultTDSServerArguments) + { + // Cast to transient fault TDS server arguments + TransientFaultTDSServerArguments ServerArguments = Arguments as TransientFaultTDSServerArguments; + + // Check if we're still going to raise transient error + if (ServerArguments.IsEnabledTransientError && RequestCounter < 1) // Fail first time, then connect + { + uint errorNumber = ServerArguments.Number; + string errorMessage = ServerArguments.Message; + + // Log request to which we're about to send a failure + TDSUtilities.Log(Arguments.Log, "Request", loginRequest); + + // Prepare ERROR token with the denial details + TDSErrorToken errorToken = new TDSErrorToken(errorNumber, 1, 20, errorMessage); + + // Log response + TDSUtilities.Log(Arguments.Log, "Response", errorToken); + + // Serialize the error token into the response packet + TDSMessage responseMessage = new TDSMessage(TDSMessageType.Response, errorToken); + + // Create DONE token + TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Error); + + // Log response + TDSUtilities.Log(Arguments.Log, "Response", doneToken); + + // Serialize DONE token into the response packet + responseMessage.Add(doneToken); + + RequestCounter++; + + // Put a single message into the collection and return it + return new TDSMessageCollection(responseMessage); + } + } + + // Return login response from the base class + return base.OnLogin7Request(session, request); + } + + public static TransientFaultTDSServer StartTestServer(bool isEnabledTransientFault, bool enableLog, uint errorNumber, [CallerMemberName] string methodName = "") + => StartServerWithQueryEngine(null, isEnabledTransientFault, enableLog, errorNumber, methodName); + + public static TransientFaultTDSServer StartServerWithQueryEngine(QueryEngine engine, bool isEnabledTransientFault, bool enableLog, uint errorNumber, [CallerMemberName] string methodName = "") + { + TransientFaultTDSServerArguments args = new TransientFaultTDSServerArguments() + { + Log = enableLog ? Console.Out : null, + IsEnabledTransientError = isEnabledTransientFault, + Number = errorNumber, + Message = GetErrorMessage(errorNumber) + }; + + TransientFaultTDSServer server = engine == null ? new TransientFaultTDSServer(args) : new TransientFaultTDSServer(engine, args); + server._endpoint = new TDSServerEndPoint(server) { ServerEndPoint = new IPEndPoint(IPAddress.Any, 0) }; + server._endpoint.EndpointName = methodName; + + // The server EventLog should be enabled as it logs the exceptions. + server._endpoint.EventLog = Console.Out; + server._endpoint.Start(); + + server.Port = server._endpoint.ServerEndPoint.Port; + return server; + } + + public void Dispose() => Dispose(true); + + private void Dispose(bool isDisposing) + { + if (isDisposing) + { + _endpoint?.Stop(); + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServerArguments.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServerArguments.cs new file mode 100644 index 0000000000..77eec68c5f --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServerArguments.cs @@ -0,0 +1,34 @@ +// 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.SqlServer.TDS.Servers +{ + public class TransientFaultTDSServerArguments : TDSServerArguments + { + /// + /// Transient error number to be raised by server. + /// + public uint Number { get; set; } + + /// + /// Transient error message to be raised by server. + /// + public string Message { get; set; } + + /// + /// Flag to consider when raising Transient error. + /// + public bool IsEnabledTransientError { get; set; } + + /// + /// Constructor to initialize + /// + public TransientFaultTDSServerArguments() + { + Number = 0; + Message = string.Empty; + IsEnabledTransientError = false; + } + } +} From e4d87e6dbef33c19965ed58d0485f3d6cd225c0b Mon Sep 17 00:00:00 2001 From: Wraith Date: Wed, 14 Apr 2021 19:00:09 +0100 Subject: [PATCH 03/87] Fix rowversion null behaviour (#998) --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 26 +++++-- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 26 +++++-- .../Data/SqlClient/LocalAppContextSwitches.cs | 26 +++++++ .../SQL/DataReaderTest/DataReaderTest.cs | 76 +++++++++++++++++++ 4 files changed, 138 insertions(+), 16 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 3fcad61f2e..e7e357ff66 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 @@ -3732,16 +3732,26 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead; _sharedState._nextColumnHeaderToRead++; // We read this one - if (isNull && columnMetaData.type != SqlDbType.Timestamp) + if (isNull) { - TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], - columnMetaData, - _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, - _parser.Connection); - - if (!readHeaderOnly) + if (columnMetaData.type == SqlDbType.Timestamp) { - _sharedState._nextColumnDataToRead++; + if (!LocalAppContextSwitches.LegacyRowVersionNullBehaviour) + { + _data[i].SetToNullOfType(SqlBuffer.StorageType.SqlBinary); + } + } + else + { + TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], + columnMetaData, + _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, + _parser.Connection); + + if (!readHeaderOnly) + { + _sharedState._nextColumnDataToRead++; + } } } else diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index bcd9858d32..17aaf3f214 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -4279,16 +4279,26 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead; _sharedState._nextColumnHeaderToRead++; // We read this one - if (isNull && columnMetaData.type != SqlDbType.Timestamp /* Maintain behavior for known bug (Dev10 479607) rejected as breaking change - See comments in GetNullSqlValue for timestamp */) + if (isNull) { - TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], - columnMetaData, - _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, - _parser.Connection); - - if (!readHeaderOnly) + if (columnMetaData.type == SqlDbType.Timestamp) { - _sharedState._nextColumnDataToRead++; + if (!LocalAppContextSwitches.LegacyRowVersionNullBehaviour) + { + _data[i].SetToNullOfType(SqlBuffer.StorageType.SqlBinary); + } + } + else + { + TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], + columnMetaData, + _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, + _parser.Connection); + + if (!readHeaderOnly) + { + _sharedState._nextColumnDataToRead++; + } } } else diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs index fe8e57022a..ed04cbc606 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs @@ -10,7 +10,11 @@ namespace Microsoft.Data.SqlClient internal static partial class LocalAppContextSwitches { internal const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking"; + internal const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehaviour"; + private static bool _makeReadAsyncBlocking; + private static bool? s_legacyRowVersionNullBehaviour; + public static bool MakeReadAsyncBlocking { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -19,5 +23,27 @@ public static bool MakeReadAsyncBlocking return AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out _makeReadAsyncBlocking) ? _makeReadAsyncBlocking : false; } } + + /// + /// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a field with type Timestamp/RowVersion + /// would return an empty byte array. This switch contols whether to preserve that behaviour on newer versions + /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned + /// + public static bool LegacyRowVersionNullBehaviour + { + get + { + if (s_legacyRowVersionNullBehaviour == null) + { + bool value = false; + if (AppContext.TryGetSwitch(LegacyRowVersionNullString, out bool providedValue)) + { + value = providedValue; + } + s_legacyRowVersionNullBehaviour = value; + } + return s_legacyRowVersionNullBehaviour.Value; + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs index 723fafc2a2..8fa25aaffb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.SqlTypes; +using System.Reflection; using System.Text; using System.Threading; using Xunit; @@ -13,6 +15,8 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class DataReaderTest { + private static object s_rowVersionLock = new object(); + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void LoadReaderIntoDataTableToTestGetSchemaTable() { @@ -288,5 +292,77 @@ public static void CheckHiddenColumns() // hidden field Assert.Contains("user_id", names, StringComparer.Ordinal); } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static void CheckNullRowVersionIsBDNull() + { + lock (s_rowVersionLock) + { + bool? originalValue = SetLegacyRowVersionNullBehaviour(false); + try + { + using (SqlConnection con = new SqlConnection(DataTestUtility.TCPConnectionString)) + { + con.Open(); + using (SqlCommand command = con.CreateCommand()) + { + command.CommandText = "select cast(null as rowversion) rv"; + using (SqlDataReader reader = command.ExecuteReader()) + { + reader.Read(); + Assert.True(reader.IsDBNull(0)); + Assert.Equal(reader[0], DBNull.Value); + } + } + } + } + finally + { + SetLegacyRowVersionNullBehaviour(originalValue); + } + } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static void CheckLegacyNullRowVersionIsEmptyArray() + { + lock (s_rowVersionLock) + { + bool? originalValue = SetLegacyRowVersionNullBehaviour(true); + try + { + using (SqlConnection con = new SqlConnection(DataTestUtility.TCPConnectionString)) + { + con.Open(); + using (SqlCommand command = con.CreateCommand()) + { + command.CommandText = "select cast(null as rowversion) rv"; + using (SqlDataReader reader = command.ExecuteReader()) + { + reader.Read(); + Assert.False(reader.IsDBNull(0)); + SqlBinary value = reader.GetSqlBinary(0); + Assert.False(value.IsNull); + Assert.Equal(0, value.Length); + Assert.NotNull(value.Value); + } + } + } + } + finally + { + SetLegacyRowVersionNullBehaviour(originalValue); + } + } + } + + private static bool? SetLegacyRowVersionNullBehaviour(bool? value) + { + Type switchesType = typeof(SqlCommand).Assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches"); + FieldInfo switchField = switchesType.GetField("s_legacyRowVersionNullBehaviour", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + bool? originalValue = (bool?)switchField.GetValue(null); + switchField.SetValue(null, value); + return originalValue; + } } } From 2e773aea35a596638bfdf3eef34accbac453f4f7 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 14 Apr 2021 15:02:56 -0700 Subject: [PATCH 04/87] Fix Socket array length to avoid possible out of bounds errors (#1031) --- .../Data/SqlClient/SNI/SNITcpHandle.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs index ddc9fdb0d3..4ca0631c51 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs @@ -327,7 +327,13 @@ private static Socket Connect(string serverName, int port, TimeSpan timeout, boo string IPv4String = null; string IPv6String = null; - Socket[] sockets = new Socket[2]; + // Returning null socket is handled by the caller function. + if(ipAddresses == null || ipAddresses.Length == 0) + { + return null; + } + + Socket[] sockets = new Socket[ipAddresses.Length]; AddressFamily[] preferedIPFamilies = new AddressFamily[] { AddressFamily.InterNetwork, AddressFamily.InterNetworkV6 }; CancellationTokenSource cts = null; @@ -360,6 +366,8 @@ void Cancel() Socket availableSocket = null; try { + int n = 0; // Socket index + // We go through the IP list twice. // In the first traversal, we only try to connect with the preferedIPFamilies[0]. // In the second traversal, we only try to connect with the preferedIPFamilies[1]. @@ -371,18 +379,18 @@ void Cancel() { if (ipAddress != null && ipAddress.AddressFamily == preferedIPFamilies[i]) { - sockets[i] = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + sockets[n] = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // enable keep-alive on socket - SetKeepAliveValues(ref sockets[i]); + SetKeepAliveValues(ref sockets[n]); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connecting to IP address {0} and port {1}", args0: ipAddress, args1: port); - sockets[i].Connect(ipAddress, port); - if (sockets[i] != null) // sockets[i] can be null if cancel callback is executed during connect() + sockets[n].Connect(ipAddress, port); + if (sockets[n] != null) // sockets[i] can be null if cancel callback is executed during connect() { - if (sockets[i].Connected) + if (sockets[n].Connected) { - availableSocket = sockets[i]; + availableSocket = sockets[n]; if (ipAddress.AddressFamily == AddressFamily.InterNetwork) { @@ -397,10 +405,11 @@ void Cancel() } else { - sockets[i].Dispose(); - sockets[i] = null; + sockets[n].Dispose(); + sockets[n] = null; } } + n++; } } catch (Exception e) From 63218db60aad681cc5c13cb6ba8576e91e05b14a Mon Sep 17 00:00:00 2001 From: Wraith Date: Wed, 14 Apr 2021 23:04:18 +0100 Subject: [PATCH 05/87] Fix derived parameters containing typename incorrectly (#1020) --- .../src/Microsoft/Data/Common/AdapterUtil.cs | 21 +++- .../Microsoft/Data/SqlClient/SqlCommand.cs | 48 +++++++- .../Microsoft/Data/SqlClient/SqlParameter.cs | 8 +- .../src/Microsoft/Data/Common/AdapterUtil.cs | 26 +++-- .../Microsoft/Data/SqlClient/SqlCommand.cs | 47 ++++++++ .../Microsoft/Data/SqlClient/SqlParameter.cs | 8 +- .../tests/ManualTests/SQL/UdtTest/UdtTest2.cs | 107 ++++++++++++++++++ tools/testsql/createUdtTestDb.sql | Bin 199880 -> 200366 bytes 8 files changed, 247 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs index 396d43caa6..19a9d2447b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs @@ -379,24 +379,33 @@ internal static Exception StreamClosed([CallerMemberName] string method = "") internal static string BuildQuotedString(string quotePrefix, string quoteSuffix, string unQuotedString) { - var resultString = new StringBuilder(); + var resultString = new StringBuilder(unQuotedString.Length + quoteSuffix.Length + quoteSuffix.Length); + AppendQuotedString(resultString, quotePrefix, quoteSuffix, unQuotedString); + return resultString.ToString(); + } + + internal static string AppendQuotedString(StringBuilder buffer, string quotePrefix, string quoteSuffix, string unQuotedString) + { + if (!string.IsNullOrEmpty(quotePrefix)) { - resultString.Append(quotePrefix); + buffer.Append(quotePrefix); } // Assuming that the suffix is escaped by doubling it. i.e. foo"bar becomes "foo""bar". if (!string.IsNullOrEmpty(quoteSuffix)) { - resultString.Append(unQuotedString.Replace(quoteSuffix, quoteSuffix + quoteSuffix)); - resultString.Append(quoteSuffix); + int start = buffer.Length; + buffer.Append(unQuotedString); + buffer.Replace(quoteSuffix, quoteSuffix + quoteSuffix, start, unQuotedString.Length); + buffer.Append(quoteSuffix); } else { - resultString.Append(unQuotedString); + buffer.Append(unQuotedString); } - return resultString.ToString(); + return buffer.ToString(); } static internal string BuildMultiPartName(string[] strings) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 2808df1443..13a66ac1c4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -3084,6 +3084,12 @@ internal void DeriveParameters() p.TypeName = r[colNames[(int)ProcParamsColIndex.TypeCatalogName]] + "." + r[colNames[(int)ProcParamsColIndex.TypeSchemaName]] + "." + r[colNames[(int)ProcParamsColIndex.TypeName]]; + + // the constructed type name above is incorrectly formatted, it should be a 2 part name not 3 + // for compatibility we can't change this because the bug has existed for a long time and been + // worked around by users, so identify that it is present and catch it later in the execution + // process once users can no longer interact with with the parameter type name + p.IsDerivedParameterTypeName = true; } // XmlSchema name for Xml types @@ -5534,6 +5540,23 @@ private void SetUpRPCParameters(_SqlRPC rpc, bool inSchema, SqlParameterCollecti { options |= TdsEnums.RPC_PARAM_DEFAULT; } + + // detect incorrectly derived type names unchanged by the caller and fix them + if (parameter.IsDerivedParameterTypeName) + { + string[] parts = MultipartIdentifier.ParseMultipartIdentifier(parameter.TypeName, "[\"", "]\"", Strings.SQL_TDSParserTableName, false); + if (parts != null && parts.Length == 4) // will always return int[4] right justified + { + if ( + parts[3] != null && // name must not be null + parts[2] != null && // schema must not be null + parts[1] != null // server should not be null or we don't need to remove it + ) + { + parameter.TypeName = QuoteIdentifier(parts.AsSpan(2, 2)); + } + } + } } rpc.userParamMap[userParamCount] = ((((long)options) << 32) | (long)index); @@ -5974,7 +5997,30 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete private static string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) { string[] strings = SqlParameter.ParseTypeName(identifier, isUdtTypeName); - return ADP.BuildMultiPartName(strings); + return QuoteIdentifier(strings); + } + + private static string QuoteIdentifier(ReadOnlySpan strings) + { + StringBuilder bld = new StringBuilder(); + + // Stitching back together is a little tricky. Assume we want to build a full multi-part name + // with all parts except trimming separators for leading empty names (null or empty strings, + // but not whitespace). Separators in the middle should be added, even if the name part is + // null/empty, to maintain proper location of the parts. + for (int i = 0; i < strings.Length; i++) + { + if (0 < bld.Length) + { + bld.Append('.'); + } + if (null != strings[i] && 0 != strings[i].Length) + { + ADP.AppendQuotedString(bld, "[", "]", strings[i]); + } + } + + return bld.ToString(); } // returns set option text to turn on format only and key info on and off diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs index da1114468e..2bab7e4dbd 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -669,7 +669,11 @@ public string UdtTypeName public string TypeName { get => _typeName ?? ADP.StrEmpty; - set => _typeName = value; + set + { + _typeName = value; + IsDerivedParameterTypeName = false; + } } /// @@ -964,6 +968,8 @@ internal string ParameterNameFixed internal INullable ValueAsINullable => _valueAsINullable; + internal bool IsDerivedParameterTypeName { get; set; } + private void CloneHelper(SqlParameter destination) { // NOTE: _parent is not cloned diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/AdapterUtil.cs index 661f49dd60..eba346f34a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/AdapterUtil.cs @@ -2334,26 +2334,34 @@ static internal string MachineName() return Environment.MachineName; } - static internal string BuildQuotedString(string quotePrefix, string quoteSuffix, string unQuotedString) + internal static string BuildQuotedString(string quotePrefix, string quoteSuffix, string unQuotedString) { - StringBuilder resultString = new StringBuilder(); - if (ADP.IsEmpty(quotePrefix) == false) + var resultString = new StringBuilder(unQuotedString.Length + quoteSuffix.Length + quoteSuffix.Length); + AppendQuotedString(resultString, quotePrefix, quoteSuffix, unQuotedString); + return resultString.ToString(); + } + + internal static string AppendQuotedString(StringBuilder buffer, string quotePrefix, string quoteSuffix, string unQuotedString) + { + if (!string.IsNullOrEmpty(quotePrefix)) { - resultString.Append(quotePrefix); + buffer.Append(quotePrefix); } // Assuming that the suffix is escaped by doubling it. i.e. foo"bar becomes "foo""bar". - if (ADP.IsEmpty(quoteSuffix) == false) + if (!string.IsNullOrEmpty(quoteSuffix)) { - resultString.Append(unQuotedString.Replace(quoteSuffix, quoteSuffix + quoteSuffix)); - resultString.Append(quoteSuffix); + int start = buffer.Length; + buffer.Append(unQuotedString); + buffer.Replace(quoteSuffix, quoteSuffix + quoteSuffix, start, unQuotedString.Length); + buffer.Append(quoteSuffix); } else { - resultString.Append(unQuotedString); + buffer.Append(unQuotedString); } - return resultString.ToString(); + return buffer.ToString(); } static internal string BuildMultiPartName(string[] strings) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index e276929dff..aa054ae5a1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -19,6 +19,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml; +using System.Buffers; using Microsoft.Data.Common; using Microsoft.Data.Sql; using Microsoft.Data.SqlClient.Server; @@ -3538,6 +3539,12 @@ internal void DeriveParameters() p.TypeName = r[colNames[(int)ProcParamsColIndex.TypeCatalogName]] + "." + r[colNames[(int)ProcParamsColIndex.TypeSchemaName]] + "." + r[colNames[(int)ProcParamsColIndex.TypeName]]; + + // the constructed type name above is incorrectly formatted, it should be a 2 part name not 3 + // for compatibility we can't change this because the bug has existed for a long time and been + // worked around by users, so identify that it is present and catch it later in the execution + // process once users can no longer interact with with the parameter type name + p.IsDerivedParameterTypeName = true; } // XmlSchema name for Xml types @@ -6464,6 +6471,23 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP { rpc.paramoptions[j] |= TdsEnums.RPC_PARAM_DEFAULT; } + + // detect incorrectly derived type names unchanged by the caller and fix them + if (parameter.IsDerivedParameterTypeName) + { + string[] parts = MultipartIdentifier.ParseMultipartIdentifier(parameter.TypeName, "[\"", "]\"", Strings.SQL_TDSParserTableName, false); + if (parts != null && parts.Length == 4) // will always return int[4] right justified + { + if ( + parts[3] != null && // name must not be null + parts[2] != null && // schema must not be null + parts[1] != null // server should not be null or we don't need to remove it + ) + { + parameter.TypeName = QuoteIdentifier(parts, 2, 2); + } + } + } } // Must set parameter option bit for LOB_COOKIE if unfilled LazyMat blob @@ -6937,6 +6961,29 @@ private string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) return ADP.BuildMultiPartName(strings); } + private static string QuoteIdentifier(string[] strings, int offset, int length) + { + StringBuilder bld = new StringBuilder(); + + // Stitching back together is a little tricky. Assume we want to build a full multi-part name + // with all parts except trimming separators for leading empty names (null or empty strings, + // but not whitespace). Separators in the middle should be added, even if the name part is + // null/empty, to maintain proper location of the parts. + for (int i = offset; i < (offset + length); i++) + { + if (0 < bld.Length) + { + bld.Append('.'); + } + if (null != strings[i] && 0 != strings[i].Length) + { + ADP.AppendQuotedString(bld, "[", "]", strings[i]); + } + } + + return bld.ToString(); + } + // returns set option text to turn on format only and key info on and off // When we are executing as a text command, then we never need // to turn off the options since they command text is executed in the scope of sp_executesql. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs index 231a635dfe..26174a9b67 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -656,7 +656,11 @@ public string UdtTypeName public string TypeName { get => _typeName ?? ADP.StrEmpty; - set => _typeName = value; + set + { + _typeName = value; + IsDerivedParameterTypeName = false; + } } /// @@ -955,6 +959,8 @@ internal string ParameterNameFixed internal INullable ValueAsINullable => _valueAsINullable; + internal bool IsDerivedParameterTypeName { get; set; } + private void CloneHelper(SqlParameter destination) { // NOTE: _parent is not cloned diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs index f34dc2833a..3437736adf 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -588,6 +588,113 @@ public void TestSqlUserDefinedAggregateAttributeMaxByteSize() () => create(-2), errorMessage); } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] + public void UDTParams_DeriveParameters_CheckAutoFixSuccess() + { + // the type and sproc must be commited to the database or this test will deadlock with a schema lock violation + // if you are missing these database entities then you should look for an updated version of the database creation script + + string sprocName = "sp_insert_customers"; + string typeName = "CustomerAddress"; + string customerAddressTypeIncorrectName = $"{DataTestUtility.UdtTestDbName}.dbo.{typeName.Trim('[', ']')}"; + string customerAddressTypeCorrectedName = $"[dbo].[{typeName.Trim('[', ']')}]"; + string customerParameterName = "@customers"; + + Address addr = Address.Parse("123 baker st || Redmond"); + DataTable table = new DataTable(); + table.Columns.Add(); + table.Columns.Add(); + table.Rows.Add("john", addr); + + using (SqlConnection connection = new SqlConnection(_connStr)) + { + connection.Open(); + using (SqlTransaction transaction = connection.BeginTransaction()) + using (SqlCommand cmd = new SqlCommand(sprocName, connection, transaction)) + { + try + { + cmd.CommandType = CommandType.StoredProcedure; + + SqlCommandBuilder.DeriveParameters(cmd); + + Assert.NotNull(cmd.Parameters); + Assert.Equal(2, cmd.Parameters.Count); // [return_value, table] + + SqlParameter p = cmd.Parameters[1]; + + Assert.Equal(customerParameterName, p.ParameterName); + Assert.Equal(SqlDbType.Structured, p.SqlDbType); + Assert.Equal(customerAddressTypeIncorrectName, p.TypeName); // the 3 part name is incorrect but needs to be maintained for compatibility + p.Value = table; + + cmd.ExecuteNonQuery(); + + Assert.Equal(customerAddressTypeCorrectedName, p.TypeName); // check that the auto fix has been applied correctly + } + finally + { + transaction.Rollback(); + } + } + } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] + public void UDTParams_DeriveParameters_CheckAutoFixOverride() + { + // the type and sproc must be commited to the database or this test will deadlock with a schema lock violation + // if you are missing these database entities then you should look for an updated version of the database creation script + + string sprocName = "sp_insert_customers"; + string typeName = "CustomerAddress"; + string customerAddressTypeIncorrectName = $"{DataTestUtility.UdtTestDbName}.dbo.{typeName.Trim('[', ']')}"; + string customerAddressTypeCorrectedName = $"[dbo].[{typeName.Trim('[', ']')}]"; + string customerParameterName = "@customers"; + + Address addr = Address.Parse("123 baker st || Redmond"); + DataTable table = new DataTable(); + table.Columns.Add(); + table.Columns.Add(); + table.Rows.Add("john", addr); + + using (SqlConnection connection = new SqlConnection(_connStr)) + { + connection.Open(); + using (SqlTransaction transaction = connection.BeginTransaction()) + using (SqlCommand cmd = new SqlCommand(sprocName, connection, transaction)) + { + try + { + cmd.CommandType = CommandType.StoredProcedure; + + SqlCommandBuilder.DeriveParameters(cmd); + + Assert.NotNull(cmd.Parameters); + Assert.Equal(2, cmd.Parameters.Count); // [return_value, table] + + SqlParameter p = cmd.Parameters[1]; + + Assert.Equal(customerParameterName, p.ParameterName); + Assert.Equal(SqlDbType.Structured, p.SqlDbType); + Assert.Equal(customerAddressTypeIncorrectName, p.TypeName); // the 3 part name is incorrect but needs to be maintained for compatibility + p.Value = table; + + p.TypeName = customerAddressTypeIncorrectName; // force using the incorrect name by manually setting it + + SqlException exception = Assert.Throws( + () => cmd.ExecuteNonQuery() + ); + Assert.Contains("Database name is not allowed", exception.Message); + } + finally + { + transaction.Rollback(); + } + } + } + } } } diff --git a/tools/testsql/createUdtTestDb.sql b/tools/testsql/createUdtTestDb.sql index 61052c75487154b3286afc8cbcf40033753108a2..5511f5a680c59982c58d5c8e188f06df16d7f86b 100644 GIT binary patch delta 337 zcmX>xlV{yro`x32EleH7(|HP+IED Y1#B*x-q^t;KRqv>QE2Pxo`x32EleH7(?75>b8Yu2VNw&{KCg#Kpa}rH?h8Kv From 5e5771d3f106245267b382c98189454ec18ce7f8 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 15 Apr 2021 10:34:03 -0700 Subject: [PATCH 06/87] Feature | Azure Identity support - Phase 1 changes (#1010) --- .../SqlAuthenticationMethod.xml | 4 +- .../SqlAuthenticationParameters.xml | 7 +- .../netcore/ref/Microsoft.Data.SqlClient.cs | 6 +- .../src/Microsoft.Data.SqlClient.csproj | 4 +- ...uthenticationProviderManager.NetCoreApp.cs | 10 +- ...thenticationProviderManager.NetStandard.cs | 11 +- .../SqlAuthenticationProviderManager.cs | 18 ++ .../SqlClient/SqlInternalConnectionTds.cs | 12 +- .../SqlClient/TdsParserStateObjectNative.cs | 1 - .../netcore/src/Resources/Strings.Designer.cs | 54 ---- .../netcore/src/Resources/Strings.resx | 18 -- .../netfx/ref/Microsoft.Data.SqlClient.cs | 4 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 8 +- .../SqlAuthenticationProviderManager.cs | 5 +- .../SqlClient/SqlInternalConnectionTds.cs | 11 +- .../netfx/src/Resources/Strings.Designer.cs | 54 ---- .../netfx/src/Resources/Strings.resx | 18 -- .../ActiveDirectoryAuthenticationProvider.cs | 111 ++++---- ...reManagedIdentityAuthenticationProvider.cs | 248 ------------------ .../SqlClient/SqlAuthenticationParameters.cs | 21 +- .../ManualTests/DataCommon/AADUtility.cs | 14 +- .../ManualTests/DataCommon/DataTestUtility.cs | 8 +- .../ConnectivityTests/AADConnectionTest.cs | 8 +- .../Config.cs | 2 +- .../config.default.json | 2 +- tools/props/Versions.props | 1 + tools/specs/Microsoft.Data.SqlClient.nuspec | 5 + 27 files changed, 161 insertions(+), 504 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureManagedIdentityAuthenticationProvider.cs diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml index 19927484fd..54b8f3d0a7 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml @@ -34,11 +34,11 @@ 6 - The authentication method uses Active Directory Managed Identity. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the object ID of the user identity. + The authentication method uses Active Directory Managed Identity. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the "client ID" of the user identity. 7 - Alias for "Active Directory Managed Identity" authentication method. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the object ID of the user identity. + Alias for "Active Directory Managed Identity" authentication method. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the "client ID" of the user identity. 8 diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml index 520882eb93..51fec0ae1f 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml @@ -12,7 +12,8 @@ The user login name/ID. The user password. The connection ID. - Initializes a new instance of the class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password and connection ID. + The connection timeout value in seconds. + Initializes a new instance of the class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password, connection ID and connection timeout value. Gets the authentication method. @@ -46,5 +47,9 @@ Gets the database name. The database name. + + Gets the connection timeout value. + The connection timeout value to be passed to Cancellation Token Source. + diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 887450bd91..2f14435381 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -108,7 +108,7 @@ public enum SqlAuthenticationMethod public partial class SqlAuthenticationParameters { /// - protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId) { } + protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId, int connectionTimeout) { } /// public Microsoft.Data.SqlClient.SqlAuthenticationMethod AuthenticationMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } /// @@ -125,6 +125,8 @@ public partial class SqlAuthenticationParameters public string ServerName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } /// public string UserId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + /// + public int ConnectionTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } /// public abstract partial class SqlAuthenticationProvider @@ -700,7 +702,7 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys /// /// for internal test only /// - [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] + [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] internal string SQLDNSCachingSupportedState { get { throw null; } } /// /// for internal test only diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index b5e1dc903c..fb55b9172b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -93,9 +93,6 @@ Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs - - Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs - Microsoft\Data\SqlClient\LocalAppContextSwitches.cs @@ -836,6 +833,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs index bf44f23184..e253829b2c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs @@ -15,7 +15,6 @@ internal partial class SqlAuthenticationProviderManager static SqlAuthenticationProviderManager() { - var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider(); SqlAuthenticationProviderConfigurationSection configurationSection = null; try @@ -35,14 +34,7 @@ static SqlAuthenticationProviderManager() } Instance = new SqlAuthenticationProviderManager(configurationSection); - var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider); + SetDefaultAuthProviders(Instance); } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs index 71b8b23269..01a84342f8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs @@ -8,17 +8,8 @@ internal partial class SqlAuthenticationProviderManager { static SqlAuthenticationProviderManager() { - var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider(); - Instance = new SqlAuthenticationProviderManager(); - var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider); + SetDefaultAuthProviders(Instance); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index f55bb9d8e4..1841f016d0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -30,6 +30,24 @@ internal partial class SqlAuthenticationProviderManager public static readonly SqlAuthenticationProviderManager Instance; + /// + /// Sets default supported Active Directory Authentication providers by the driver + /// on the SqlAuthenticationProviderManager instance. + /// + private static void SetDefaultAuthProviders(SqlAuthenticationProviderManager instance) + { + if (instance != null) + { + var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(instance._applicationClientId); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider); + } + } /// /// Constructor. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 9b4358f3ce..6f352799a1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2292,7 +2292,6 @@ internal bool TryGetFedAuthTokenLocked(SqlFedAuthInfo fedAuthInfo, DbConnectionP /// SqlFedAuthToken internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { - Debug.Assert(fedAuthInfo != null, "fedAuthInfo should not be null."); // No:of milliseconds to sleep for the inital back off. @@ -2324,7 +2323,8 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) authority: fedAuthInfo.stsurl, serverName: ConnectionOptions.DataSource, databaseName: ConnectionOptions.InitialCatalog) - .WithConnectionId(_clientConnectionId); + .WithConnectionId(_clientConnectionId) + .WithConnectionTimeout(ConnectionOptions.ConnectTimeout); switch (ConnectionOptions.Authentication) { case SqlAuthenticationMethod.ActiveDirectoryIntegrated: @@ -2346,7 +2346,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) } else { - Task.Run(() => _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; @@ -2361,7 +2361,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) else { authParamsBuilder.WithUserId(ConnectionOptions.UserID); - Task.Run(() => _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; @@ -2377,13 +2377,13 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { username = _credential.UserId; authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); - Task.Run(() => _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken(); } else { username = ConnectionOptions.UserID; authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); - Task.Run(() => _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken(); } _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 1b7deb2a0b..34c7910dde 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -96,7 +96,6 @@ internal override void AssignPendingDNSInfo(string userProtocol, string DNSCache result = SNINativeMethodWrapper.SniGetConnectionPort(Handle, ref portFromSNI); Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionPort"); - result = SNINativeMethodWrapper.SniGetConnectionIPString(Handle, ref IPStringFromSNI); Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionIPString"); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index a351132431..8a95a52351 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -897,60 +897,6 @@ internal class Strings { } } - /// - /// Looks up a localized string similar to Access token could not be acquired.. - /// - internal static string Azure_GenericErrorMessage { - get { - return ResourceManager.GetString("Azure_GenericErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to connect to the Managed Identity endpoint. Please check that you are running on an Azure resource that has Identity setup.. - /// - internal static string Azure_IdentityEndpointNotListening { - get { - return ResourceManager.GetString("Azure_IdentityEndpointNotListening", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tried to get token using Managed Identity.. - /// - internal static string Azure_ManagedIdentityUsed { - get { - return ResourceManager.GetString("Azure_ManagedIdentityUsed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to connect to the Instance Metadata Service (IMDS). Skipping request to the Managed Identity token endpoint.. - /// - internal static string Azure_MetadataEndpointNotListening { - get { - return ResourceManager.GetString("Azure_MetadataEndpointNotListening", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Received a non-retryable error.. - /// - internal static string Azure_NonRetryableError { - get { - return ResourceManager.GetString("Azure_NonRetryableError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed after 5 retries.. - /// - internal static string Azure_RetryFailure { - get { - return ResourceManager.GetString("Azure_RetryFailure", resourceCulture); - } - } - /// /// Looks up a localized string similar to .database.chinacloudapi.cn. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index 6854ef7fad..5dd36f38ac 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1914,24 +1914,6 @@ Cannot use 'Authentication={0}', if the Credential property has been set. - - Access token could not be acquired. - - - Unable to connect to the Managed Identity endpoint. Please check that you are running on an Azure resource that has Identity setup. - - - Tried to get token using Managed Identity. - - - Unable to connect to the Instance Metadata Service (IMDS). Skipping request to the Managed Identity token endpoint. - - - Received a non-retryable error. - - - Failed after 5 retries. - Value '{0}' is out of range. Must be between {1} and {2}. diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index f8345315fd..23c88ff5b5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -126,7 +126,7 @@ public enum SqlAuthenticationMethod public partial class SqlAuthenticationParameters { /// - protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId) { } + protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId, int connectionTimeout) { } /// public Microsoft.Data.SqlClient.SqlAuthenticationMethod AuthenticationMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } /// @@ -143,6 +143,8 @@ public partial class SqlAuthenticationParameters public string ServerName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } /// public string UserId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + /// + public int ConnectionTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } /// public abstract partial class SqlAuthenticationProvider diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 2ceeed013b..e6937ea1c4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -154,9 +154,6 @@ Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs - - Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs - Microsoft\Data\SqlClient\EnclaveDelegate.cs @@ -581,6 +578,9 @@ All runtime; build; native; contentfiles; analyzers; buildtransitive + + $(AzureIdentityVersion) + $(MicrosoftIdentityClientVersion) @@ -602,4 +602,4 @@ - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index 0280954005..561c0cd101 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -25,7 +25,6 @@ internal class SqlAuthenticationProviderManager static SqlAuthenticationProviderManager() { - var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider(); SqlAuthenticationProviderConfigurationSection configurationSection = null; try { @@ -50,8 +49,8 @@ static SqlAuthenticationProviderManager() Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider); + Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider); + Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider); } public static readonly SqlAuthenticationProviderManager Instance; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 10970c5453..4e268a953b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2771,7 +2771,8 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) authority: fedAuthInfo.stsurl, serverName: ConnectionOptions.DataSource, databaseName: ConnectionOptions.InitialCatalog) - .WithConnectionId(_clientConnectionId); + .WithConnectionId(_clientConnectionId) + .WithConnectionTimeout(ConnectionOptions.ConnectTimeout); switch (ConnectionOptions.Authentication) { case SqlAuthenticationMethod.ActiveDirectoryIntegrated: @@ -2782,7 +2783,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) } else { - Task.Run(() => fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = fedAuthToken; } break; @@ -2797,7 +2798,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) else { authParamsBuilder.WithUserId(ConnectionOptions.UserID); - Task.Run(() => fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = fedAuthToken; } break; @@ -2813,13 +2814,13 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { username = _credential.UserId; authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); - Task.Run(() => fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken(); } else { username = ConnectionOptions.UserID; authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); - Task.Run(() => fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken(); } _activeDirectoryAuthTimeoutRetryHelper.CachedToken = fedAuthToken; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index af7390b2a9..666ff47326 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -1833,60 +1833,6 @@ internal class Strings { } } - /// - /// Looks up a localized string similar to Access token could not be acquired.. - /// - internal static string Azure_GenericErrorMessage { - get { - return ResourceManager.GetString("Azure_GenericErrorMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to connect to the Managed Identity endpoint. Please check that you are running on an Azure resource that has Identity setup.. - /// - internal static string Azure_IdentityEndpointNotListening { - get { - return ResourceManager.GetString("Azure_IdentityEndpointNotListening", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tried to get token using Managed Identity.. - /// - internal static string Azure_ManagedIdentityUsed { - get { - return ResourceManager.GetString("Azure_ManagedIdentityUsed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to connect to the Instance Metadata Service (IMDS). Skipping request to the Managed Identity token endpoint.. - /// - internal static string Azure_MetadataEndpointNotListening { - get { - return ResourceManager.GetString("Azure_MetadataEndpointNotListening", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Received a non-retryable error.. - /// - internal static string Azure_NonRetryableError { - get { - return ResourceManager.GetString("Azure_NonRetryableError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed after 5 retries.. - /// - internal static string Azure_RetryFailure { - get { - return ResourceManager.GetString("Azure_RetryFailure", resourceCulture); - } - } - /// /// Looks up a localized string similar to .database.chinacloudapi.cn. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index d1b907c6e9..6f12cd0ac6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4584,24 +4584,6 @@ Cannot use 'Authentication={0}', if the Credential property has been set. - - Access token could not be acquired. - - - Unable to connect to the Managed Identity endpoint. Please check that you are running on an Azure resource that has Identity setup. - - - Tried to get token using Managed Identity. - - - Unable to connect to the Instance Metadata Service (IMDS). Skipping request to the Managed Identity token endpoint. - - - Received a non-retryable error. - - - Failed after 5 retries. - Unexpected type detected on deserialize. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs index 29500ef5b4..65aeec11c8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -8,6 +8,8 @@ using System.Security; using System.Threading; using System.Threading.Tasks; +using Azure.Core; +using Azure.Identity; using Microsoft.Identity.Client; using Microsoft.Identity.Client.Extensibility; @@ -69,7 +71,9 @@ public override bool IsSupported(SqlAuthenticationMethod authentication) || authentication == SqlAuthenticationMethod.ActiveDirectoryPassword || authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal - || authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow; + || authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow + || authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity + || authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; } /// @@ -99,24 +103,37 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) #endif /// - public override Task AcquireTokenAsync(SqlAuthenticationParameters parameters) => Task.Run(async () => + public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters) { - AuthenticationResult result; + CancellationTokenSource cts = new CancellationTokenSource(); + + // Use Connection timeout value to cancel token acquire request after certain period of time. + cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds + string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix; string[] scopes = new string[] { scope }; + int seperatorIndex = parameters.Authority.LastIndexOf('/'); + string tenantId = parameters.Authority.Substring(seperatorIndex + 1); + string authority = parameters.Authority.Remove(seperatorIndex + 1); + + TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions() { AuthorityHost = new Uri(authority) }; + TokenRequestContext tokenRequestContext = new TokenRequestContext(scopes); + + if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryMSI) + { + string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId; + AccessToken accessToken = await new ManagedIdentityCredential(clientId, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Managed Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn); + return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); + } + + AuthenticationResult result; if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal) { - IConfidentialClientApplication ccApp = ConfidentialClientApplicationBuilder.Create(parameters.UserId) - .WithAuthority(parameters.Authority) - .WithClientSecret(parameters.Password) - .WithClientName(Common.DbConnectionStringDefaults.ApplicationName) - .WithClientVersion(Common.ADP.GetAssemblyVersion().ToString()) - .Build(); - - result = ccApp.AcquireTokenForClient(scopes).ExecuteAsync().Result; - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Service Principal auth mode. Expiry Time: {0}", result.ExpiresOn); - return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn); + AccessToken accessToken = await new ClientSecretCredential(tenantId, parameters.UserId, parameters.Password, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Service Principal auth mode. Expiry Time: {0}", accessToken.ExpiresOn); + return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); } /* @@ -149,18 +166,18 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) { if (!string.IsNullOrEmpty(parameters.UserId)) { - result = app.AcquireTokenByIntegratedWindowsAuth(scopes) + result = await app.AcquireTokenByIntegratedWindowsAuth(scopes) .WithCorrelationId(parameters.ConnectionId) .WithUsername(parameters.UserId) - .ExecuteAsync().Result; + .ExecuteAsync(cancellationToken: cts.Token); } else { - result = app.AcquireTokenByIntegratedWindowsAuth(scopes) + result = await app.AcquireTokenByIntegratedWindowsAuth(scopes) .WithCorrelationId(parameters.ConnectionId) - .ExecuteAsync().Result; + .ExecuteAsync(cancellationToken: cts.Token); } - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result.ExpiresOn); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result?.ExpiresOn); } else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryPassword) { @@ -168,10 +185,11 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) foreach (char c in parameters.Password) password.AppendChar(c); password.MakeReadOnly(); - result = app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, password) + + result = await app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, password) .WithCorrelationId(parameters.ConnectionId) - .ExecuteAsync().Result; - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result.ExpiresOn); + .ExecuteAsync(cancellationToken: cts.Token); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result?.ExpiresOn); } else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow) @@ -194,23 +212,23 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) { // If 'account' is available in 'app', we use the same to acquire token silently. // Read More on API docs: https://docs.microsoft.com/dotnet/api/microsoft.identity.client.clientapplicationbase.acquiretokensilent - result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync(); - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result.ExpiresOn); + result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync(cancellationToken: cts.Token); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn); } catch (MsalUiRequiredException) { // An 'MsalUiRequiredException' is thrown in the case where an interaction is required with the end user of the application, // for instance, if no refresh token was in the cache, or the user needs to consent, or re-sign-in (for instance if the password expired), // or the user needs to perform two factor authentication. - result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod); - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result.ExpiresOn); + result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn); } } else { // If no existing 'account' is found, we request user to sign in interactively. - result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod); - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result.ExpiresOn); + result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn); } } else @@ -220,36 +238,35 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) } return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn); - }); - + } private async Task AcquireTokenInteractiveDeviceFlowAsync(IPublicClientApplication app, string[] scopes, Guid connectionId, string userId, - SqlAuthenticationMethod authenticationMethod) + SqlAuthenticationMethod authenticationMethod, CancellationTokenSource cts) { - CancellationTokenSource cts = new CancellationTokenSource(); -#if NETCOREAPP - /* - * On .NET Core, MSAL will start the system browser as a separate process. MSAL does not have control over this browser, - * but once the user finishes authentication, the web page is redirected in such a way that MSAL can intercept the Uri. - * MSAL cannot detect if the user navigates away or simply closes the browser. Apps using this technique are encouraged - * to define a timeout (via CancellationToken). We recommend a timeout of at least a few minutes, to take into account - * cases where the user is prompted to change password or perform 2FA. - * - * https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/System-Browser-on-.Net-Core#system-browser-experience - */ - cts.CancelAfter(180000); -#endif try { if (authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive) { + CancellationTokenSource ctsInteractive = new CancellationTokenSource(); +#if NETCOREAPP + /* + * On .NET Core, MSAL will start the system browser as a separate process. MSAL does not have control over this browser, + * but once the user finishes authentication, the web page is redirected in such a way that MSAL can intercept the Uri. + * MSAL cannot detect if the user navigates away or simply closes the browser. Apps using this technique are encouraged + * to define a timeout (via CancellationToken). We recommend a timeout of at least a few minutes, to take into account + * cases where the user is prompted to change password or perform 2FA. + * + * https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/System-Browser-on-.Net-Core#system-browser-experience + */ + ctsInteractive.CancelAfter(180000); +#endif if (_customWebUI != null) { return await app.AcquireTokenInteractive(scopes) .WithCorrelationId(connectionId) .WithCustomWebUi(_customWebUI) .WithLoginHint(userId) - .ExecuteAsync(cts.Token); + .ExecuteAsync(ctsInteractive.Token); } else { @@ -273,13 +290,15 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) return await app.AcquireTokenInteractive(scopes) .WithCorrelationId(connectionId) .WithLoginHint(userId) - .ExecuteAsync(cts.Token); + .ExecuteAsync(ctsInteractive.Token); } } else { AuthenticationResult result = await app.AcquireTokenWithDeviceCode(scopes, - deviceCodeResult => _deviceCodeFlowCallback(deviceCodeResult)).ExecuteAsync(); + deviceCodeResult => _deviceCodeFlowCallback(deviceCodeResult)) + .WithCorrelationId(connectionId) + .ExecuteAsync(cancellationToken: cts.Token); return result; } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureManagedIdentityAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureManagedIdentityAuthenticationProvider.cs deleted file mode 100644 index 6f543d909e..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureManagedIdentityAuthenticationProvider.cs +++ /dev/null @@ -1,248 +0,0 @@ -// 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.Net.Http; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Data.SqlClient -{ - internal sealed class AzureManagedIdentityAuthenticationProvider : SqlAuthenticationProvider - { - // HttpClient is intended to be instantiated once and re-used throughout the life of an application. -#if NETFRAMEWORK - private static readonly HttpClient s_httpClient = new HttpClient(); -#else - private static readonly HttpClient s_httpClient = new HttpClient(new HttpClientHandler() { CheckCertificateRevocationList = true }); -#endif - - private const string AzureSystemApiVersion = "&api-version=2019-08-01"; - private const string AzureVmImdsApiVersion = "&api-version=2018-02-01"; - private const string AccessToken = "access_token"; - private const string Expiry = "expires_on"; - private const int FileTimeLength = 10; - - private const int DefaultRetryTimeout = 0; - private const int DefaultMaxRetryCount = 5; - - // Azure Instance Metadata Service (IMDS) endpoint - private const string AzureVmImdsEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token"; - - // Timeout for Azure IMDS probe request - internal const int AzureVmImdsProbeTimeoutInSeconds = 2; - internal readonly TimeSpan _azureVmImdsProbeTimeout = TimeSpan.FromSeconds(AzureVmImdsProbeTimeoutInSeconds); - - // Configurable timeout for MSI retry logic - internal readonly int _retryTimeoutInSeconds = DefaultRetryTimeout; - internal readonly int _maxRetryCount = DefaultMaxRetryCount; - - // Reference: https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http - public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters) - { - HttpClient httpClient = s_httpClient; - - try - { - // Check if App Services MSI is available. If both these environment variables are set, then it is. - string msiEndpoint = Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT"); - string msiHeader = Environment.GetEnvironmentVariable("IDENTITY_HEADER"); - - var isAppServicesMsiAvailable = !string.IsNullOrWhiteSpace(msiEndpoint) && !string.IsNullOrWhiteSpace(msiHeader); - - // if App Service MSI is not available then Azure VM IMDS may be available, test with a probe request - if (!isAppServicesMsiAvailable) - { - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | This environment is not identified as an Azure App Service environment. Proceeding to validate Azure VM IMDS endpoint availability."); - using (var internalTokenSource = new CancellationTokenSource()) - using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(internalTokenSource.Token, default)) - { - HttpRequestMessage imdsProbeRequest = new HttpRequestMessage(HttpMethod.Get, AzureVmImdsEndpoint); - - try - { - internalTokenSource.CancelAfter(_azureVmImdsProbeTimeout); - await httpClient.SendAsync(imdsProbeRequest, linkedTokenSource.Token).ConfigureAwait(false); - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | The Instance Metadata Service (IMDS) service endpoint is accessible. Proceeding to acquire access token."); - } - catch (OperationCanceledException) - { - // request to IMDS timed out (internal cancellation token canceled), neither Azure VM IMDS nor App Services MSI are available - if (internalTokenSource.Token.IsCancellationRequested) - { - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | The Instance Metadata Service (IMDS) service endpoint is not accessible."); - // Throw error: Tried to get token using Managed Identity. Unable to connect to the Instance Metadata Service (IMDS). Skipping request to the Managed Service Identity (MSI) token endpoint. - throw SQL.Azure_ManagedIdentityException($"{Strings.Azure_ManagedIdentityUsed} {Strings.Azure_MetadataEndpointNotListening}"); - } - - throw; - } - } - } - else - { - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | This environment is identified as an Azure App Service environment. Proceeding to acquire access token from Endpoint URL: {0}", msiEndpoint); - } - - string objectIdParameter = string.Empty; - - // If user assigned managed identity is specified, include object ID parameter in request - if (!string.IsNullOrWhiteSpace(parameters.UserId)) - { - objectIdParameter = $"&object_id={Uri.EscapeDataString(parameters.UserId)}"; - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Identity Object id received and will be used for acquiring access token {0}", parameters.UserId); - } - - // Craft request as per the MSI protocol - var requestUrl = isAppServicesMsiAvailable - ? $"{msiEndpoint}?resource={parameters.Resource}{objectIdParameter}{AzureSystemApiVersion}" - : $"{AzureVmImdsEndpoint}?resource={parameters.Resource}{objectIdParameter}{AzureVmImdsApiVersion}"; - - HttpResponseMessage response = null; - - try - { - response = await httpClient.SendAsyncWithRetry(getRequestMessage, _retryTimeoutInSeconds, _maxRetryCount, default).ConfigureAwait(false); - HttpRequestMessage getRequestMessage() - { - HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl); - - if (isAppServicesMsiAvailable) - { - request.Headers.Add("X-IDENTITY-HEADER", msiHeader); - } - else - { - request.Headers.Add("Metadata", "true"); - } - - return request; - } - } - catch (HttpRequestException) - { - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Failed after 5 retries. Unable to connect to the Managed Service Identity (MSI) endpoint."); - // Throw error: Tried to get token using Managed Service Identity. Failed after 5 retries. Unable to connect to the Managed Service Identity (MSI) endpoint. Please check that you are running on an Azure resource that has MSI setup. - throw SQL.Azure_ManagedIdentityException($"{Strings.Azure_ManagedIdentityUsed} {Strings.Azure_RetryFailure} {Strings.Azure_IdentityEndpointNotListening}"); - } - - // If the response is successful, it should have JSON response with an access_token field - if (response.IsSuccessStatusCode) - { - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Successful response received. Status Code {0}", response.StatusCode); - string jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - int accessTokenStartIndex = jsonResponse.IndexOf(AccessToken) + AccessToken.Length + 3; - var imdsAccessToken = jsonResponse.Substring(accessTokenStartIndex, jsonResponse.IndexOf('"', accessTokenStartIndex) - accessTokenStartIndex); - var expiresin = jsonResponse.Substring(jsonResponse.IndexOf(Expiry) + Expiry.Length + 3, FileTimeLength); - DateTime expiryTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(long.Parse(expiresin)); - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Access Token received. Expiry Time: {0}", expiryTime); - return new SqlAuthenticationToken(imdsAccessToken, expiryTime); - } - - // RetryFailure : Failed after 5 retries. - // NonRetryableError : Received a non-retryable error. - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Request to acquire access token failed with status code {0}", response.StatusCode); - string errorStatusDetail = response.IsRetryableStatusCode() - ? Strings.Azure_RetryFailure - : Strings.Azure_NonRetryableError; - - string errorText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Error occurred while acquiring access token: {0} Identity Response Code: {1}, Response: {2}", errorStatusDetail, response.StatusCode, errorText); - throw SQL.Azure_ManagedIdentityException($"{errorStatusDetail} Identity Response Code: {response.StatusCode}, Response: {errorText}"); - } - catch (Exception exp) - { - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Error occurred while acquiring access token: {0}", exp.Message); - if (exp is SqlException) - throw; - // Throw error: Access token could not be acquired. {exp.Message} - throw SQL.Azure_ManagedIdentityException($"{Strings.Azure_ManagedIdentityUsed} {Strings.Azure_GenericErrorMessage} {exp.Message}"); - } - } - - public override bool IsSupported(SqlAuthenticationMethod authentication) - { - return authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity - || authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; - } - } - - #region IMDS Retry Helper - internal static class SqlManagedIdentityRetryHelper - { - internal const int DeltaBackOffInSeconds = 2; - internal const string RetryTimeoutError = "Reached retry timeout limit set by MsiRetryTimeout parameter in connection string."; - - internal static bool IsRetryableStatusCode(this HttpResponseMessage response) - { - // 404 NotFound, 429 TooManyRequests, and 5XX server error status codes are retryable - return Regex.IsMatch(((int)response.StatusCode).ToString(), @"404|429|5\d{2}"); - } - - /// - /// Implements recommended retry guidance here: https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#retry-guidance - /// - internal static async Task SendAsyncWithRetry(this HttpClient httpClient, Func getRequest, int retryTimeoutInSeconds, int maxRetryCount, CancellationToken cancellationToken) - { - using (var timeoutTokenSource = new CancellationTokenSource()) - using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, cancellationToken)) - { - try - { - // if retry timeout is configured, configure cancellation after timeout period elapses - if (retryTimeoutInSeconds > 0) - { - timeoutTokenSource.CancelAfter(TimeSpan.FromSeconds(retryTimeoutInSeconds)); - } - - var attempts = 0; - var backoffTimeInSecs = 0; - HttpResponseMessage response; - - while (true) - { - attempts++; - - try - { - response = await httpClient.SendAsync(getRequest(), linkedTokenSource.Token).ConfigureAwait(false); - - if (response.IsSuccessStatusCode || !response.IsRetryableStatusCode() || attempts == maxRetryCount) - { - break; - } - } - catch (HttpRequestException e) - { - if (attempts == maxRetryCount) - { - throw; - } - SqlClientEventSource.Log.TryTraceEvent("SendAsyncWithRetry | Exception occurred {0} | Attempting retry: {1} of {2}", e.Message, attempts, maxRetryCount); - } - - // use recommended exponential backoff strategy, and use linked token wait handle so caller or retry timeout is still able to cancel - backoffTimeInSecs += (int)Math.Pow(DeltaBackOffInSeconds, attempts); - linkedTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(backoffTimeInSecs)); - linkedTokenSource.Token.ThrowIfCancellationRequested(); - } - - return response; - } - catch (OperationCanceledException) - { - if (timeoutTokenSource.IsCancellationRequested) - { - throw new TimeoutException(RetryTimeoutError); - } - - throw; - } - } - } - } - #endregion -} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParameters.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParameters.cs index 28942ae39f..745ba716e3 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParameters.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParameters.cs @@ -5,6 +5,7 @@ using System; using System.Runtime.InteropServices; using System.Security; +using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { @@ -36,6 +37,9 @@ public class SqlAuthenticationParameters /// public string DatabaseName { get; } + /// + public int ConnectionTimeout = ADP.DefaultConnectionTimeout; + /// protected SqlAuthenticationParameters( SqlAuthenticationMethod authenticationMethod, @@ -45,7 +49,8 @@ public class SqlAuthenticationParameters string authority, string userId, string password, - Guid connectionId) + Guid connectionId, + int connectionTimeout) { AuthenticationMethod = authenticationMethod; ServerName = serverName; @@ -55,6 +60,7 @@ public class SqlAuthenticationParameters UserId = userId; Password = password; ConnectionId = connectionId; + ConnectionTimeout = connectionTimeout; } /// @@ -70,6 +76,7 @@ internal class Builder private string _userId; private string _password; private Guid _connectionId = Guid.NewGuid(); + private int _connectionTimeout = ADP.DefaultConnectionTimeout; /// /// Implicitly converts to . @@ -84,7 +91,8 @@ internal class Builder authority: builder._authority, userId: builder._userId, password: builder._password, - connectionId: builder._connectionId); + connectionId: builder._connectionId, + connectionTimeout: builder._connectionTimeout); } /// @@ -132,6 +140,15 @@ public Builder WithConnectionId(Guid connectionId) return this; } + /// + /// Set connection timeout. + /// + public Builder WithConnectionTimeout(int timeout) + { + _connectionTimeout = timeout; + return this; + } + internal Builder(SqlAuthenticationMethod authenticationMethod, string resource, string authority, string serverName, string databaseName) { _authenticationMethod = authenticationMethod; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs index 228217c039..6abff25d13 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs @@ -26,8 +26,8 @@ public static async Task AzureActiveDirectoryAuthenticationCallback(stri return result.AccessToken; } - public static async Task GetManagedIdentityToken(string objectId = null) => - await new MockManagedIdentityTokenProvider().AcquireTokenAsync(objectId).ConfigureAwait(false); + public static async Task GetManagedIdentityToken(string clientId = null) => + await new MockManagedIdentityTokenProvider().AcquireTokenAsync(clientId).ConfigureAwait(false); } @@ -59,20 +59,20 @@ internal class MockManagedIdentityTokenProvider internal readonly int _retryTimeoutInSeconds = DefaultRetryTimeout; internal readonly int _maxRetryCount = DefaultMaxRetryCount; - public async Task AcquireTokenAsync(string objectId = null) + public async Task AcquireTokenAsync(string clientId = null) { // Use the httpClient specified in the constructor. If it was not specified in the constructor, use the default httpClient. HttpClient httpClient = s_defaultHttpClient; try { - // If user assigned managed identity is specified, include object ID parameter in request - string objectIdParameter = objectId != null - ? $"&object_id={objectId}" + // If user assigned managed identity is specified, include client Id parameter in request + string clientIdParameter = !string.IsNullOrEmpty(clientId) + ? $"&client_id={clientId}" : string.Empty; // Craft request as per the MSI protocol - var requestUrl = $"{AzureVmImdsEndpoint}?resource={Resource}{objectIdParameter}{AzureVmImdsApiVersion}"; + var requestUrl = $"{AzureVmImdsEndpoint}?resource={Resource}{clientIdParameter}{AzureVmImdsApiVersion}"; HttpResponseMessage response = null; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 80701905f3..58dd79df04 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -53,7 +53,7 @@ public static class DataTestUtility public static readonly string DNSCachingServerTR = null; // this is for the tenant ring public static readonly bool IsDNSCachingSupportedCR = false; // this is for the control ring public static readonly bool IsDNSCachingSupportedTR = false; // this is for the tenant ring - public static readonly string UserManagedIdentityObjectId = null; + public static readonly string UserManagedIdentityClientId = null; public static readonly string EnclaveAzureDatabaseConnString = null; public static bool ManagedIdentitySupported = true; @@ -93,7 +93,7 @@ static DataTestUtility() IsDNSCachingSupportedCR = c.IsDNSCachingSupportedCR; IsDNSCachingSupportedTR = c.IsDNSCachingSupportedTR; EnclaveAzureDatabaseConnString = c.EnclaveAzureDatabaseConnString; - UserManagedIdentityObjectId = c.UserManagedIdentityObjectId; + UserManagedIdentityClientId = c.UserManagedIdentityClientId; System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; @@ -459,8 +459,8 @@ public static string GetUserIdentityAccessToken() { if (true == ManagedIdentitySupported && null == AADUserIdentityAccessToken && IsAADPasswordConnStrSetup()) { - // Pass User Assigned Managed Identity Object Id here. - AADUserIdentityAccessToken = AADUtility.GetManagedIdentityToken(UserManagedIdentityObjectId).GetAwaiter().GetResult(); + // Pass User Assigned Managed Identity Client Id here. + AADUserIdentityAccessToken = AADUtility.GetManagedIdentityToken(UserManagedIdentityClientId).GetAwaiter().GetResult(); if (AADUserIdentityAccessToken == null) { ManagedIdentitySupported = false; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs index 8036f5aaf6..d5ea380f3c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -164,7 +164,7 @@ public static void AADPasswordWithWrongPassword() AggregateException e = Assert.Throws(() => ConnectAndDisconnect(connStr)); string expectedMessage = "ID3242: The security token could not be authenticated or authorized."; - Assert.Contains(expectedMessage, e.InnerException.InnerException.InnerException.Message); + Assert.Contains(expectedMessage, e.InnerException.InnerException.Message); } @@ -388,7 +388,7 @@ public static void ActiveDirectoryManagedIdentityWithInvalidUserIdMustFail(strin AggregateException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); - string expectedMessage = "Response: {\"error\":\"invalid_request\",\"error_description\":\"Identity not found\"}"; + string expectedMessage = "ManagedIdentityCredential authentication unavailable, the requested identity has not been assigned to this resource."; Assert.Contains(expectedMessage, e.GetBaseException().Message); } @@ -491,7 +491,7 @@ public static void UserAssigned_ManagedIdentityTest() { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys) + - $"Authentication=Active Directory Managed Identity; User Id={DataTestUtility.UserManagedIdentityObjectId};"; + $"Authentication=Active Directory Managed Identity; User Id={DataTestUtility.UserManagedIdentityClientId};"; ConnectAndDisconnect(connStr); } @@ -543,7 +543,7 @@ public static void Azure_UserManagedIdentityTest() { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; string connectionString = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.TCPConnectionString, removeKeys) - + $"Authentication=Active Directory Managed Identity; User Id={DataTestUtility.UserManagedIdentityObjectId}"; + + $"Authentication=Active Directory Managed Identity; User Id={DataTestUtility.UserManagedIdentityClientId}"; using (SqlConnection conn = new SqlConnection(connectionString)) { diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs index de6fffd6ae..8379b81baa 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs @@ -36,7 +36,7 @@ public class Config public bool IsDNSCachingSupportedCR = false; // this is for the control ring public bool IsDNSCachingSupportedTR = false; // this is for the tenant ring public string EnclaveAzureDatabaseConnString = null; - public string UserManagedIdentityObjectId = null; + public string UserManagedIdentityClientId = null; public static Config Load(string configPath = @"config.json") { diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json index 11ee9f25e5..4c725d8555 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json @@ -20,5 +20,5 @@ "UseManagedSNIOnWindows": false, "IsAzureSynapse": false, "EnclaveAzureDatabaseConnString": "", - "UserManagedIdentityObjectId": "" + "UserManagedIdentityClientId": "" } diff --git a/tools/props/Versions.props b/tools/props/Versions.props index fc96c72988..b02ec3f871 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -17,6 +17,7 @@ + 1.3.0 4.21.1 6.8.0 6.8.0 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 6f27e7e6b1..13e6d26bdf 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -29,6 +29,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -41,6 +42,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -53,6 +55,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -65,6 +68,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -77,6 +81,7 @@ When using NuGet 3.x this package requires at least version 3.4. + From 976835177b0d64957a3d756bac8b2387227baea5 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Thu, 15 Apr 2021 11:03:25 -0700 Subject: [PATCH 07/87] Remove RegisterColumnEncryptionKeyStoreProvidersOnConnection (#1032) --- .../SqlConnection.xml | 18 ------- .../netcore/ref/Microsoft.Data.SqlClient.cs | 2 - .../Microsoft/Data/SqlClient/SqlConnection.cs | 35 ------------ .../netfx/ref/Microsoft.Data.SqlClient.cs | 2 - .../Microsoft/Data/SqlClient/SqlConnection.cs | 32 ----------- .../ExceptionRegisterKeyStoreProvider.cs | 54 ------------------- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 22 -------- 7 files changed, 165 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 7eee99980d..4cc4448f8b 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -1052,24 +1052,6 @@ GO This function was called more than once. - - Dictionary of custom column encryption key providers - Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the static methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. - - A null dictionary was provided. - - -or- - - A string key in the dictionary was null or empty. - - -or- - - An EncryptionKeyStoreProvider value in the dictionary was null. - - - A string key in the dictionary started with "MSSQL_". This prefix is reserved for system providers. - - Gets or sets a value that specifies the diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 2f14435381..b6956df25b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -689,8 +689,6 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys public static System.Collections.Generic.IDictionary> ColumnEncryptionTrustedMasterKeyPaths { get { throw null; } } /// public static void RegisterColumnEncryptionKeyStoreProviders(System.Collections.Generic.IDictionary customProviders) { } - /// - public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(System.Collections.Generic.IDictionary customProviders) { } /// [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 1fcd1f1256..33fd700673 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -91,11 +91,6 @@ private enum CultureCheckState : uint /// private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; - /// - /// Per-connection custom providers. It can be provided by the user and can be set more than once. - /// - private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; - /// /// Dictionary object holding trusted key paths for various SQL Servers. /// Key to the dictionary is a SQL Server Name @@ -239,13 +234,6 @@ internal static bool TryGetColumnEncryptionKeyStoreProvider(string providerName, return true; } - // instance-level custom provider cache takes precedence over global cache - if (connection._customColumnEncryptionKeyStoreProviders != null && - connection._customColumnEncryptionKeyStoreProviders.Count > 0) - { - return connection._customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); - } - lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false @@ -276,11 +264,6 @@ internal static List GetColumnEncryptionSystemKeyStoreProviders() /// Combined list of provider names internal static List GetColumnEncryptionCustomKeyStoreProviders(SqlConnection connection) { - if (connection._customColumnEncryptionKeyStoreProviders != null && - connection._customColumnEncryptionKeyStoreProviders.Count > 0) - { - return connection._customColumnEncryptionKeyStoreProviders.Keys.ToList(); - } if (s_globalCustomColumnEncryptionKeyStoreProviders != null) { return s_globalCustomColumnEncryptionKeyStoreProviders.Keys.ToList(); @@ -323,24 +306,6 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary - public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) - { - ValidateCustomProviders(customProviders); - - // Create a temporary dictionary and then add items from the provided dictionary. - // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs - // in the provided customerProviders dictionary. - Dictionary customColumnEncryptionKeyStoreProviders = - new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); - - // Set the dictionary to the ReadOnly dictionary. - // This method can be called more than once. Re-registering a new collection will replace the - // old collection of providers. - _customColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; - } - private static void ValidateCustomProviders(IDictionary customProviders) { // Throw when the provided dictionary is null. diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 23c88ff5b5..16227cc0c8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -850,8 +850,6 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys public override System.Threading.Tasks.Task OpenAsync(System.Threading.CancellationToken cancellationToken) { throw null; } /// public static void RegisterColumnEncryptionKeyStoreProviders(System.Collections.Generic.IDictionary customProviders) { } - /// - public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(System.Collections.Generic.IDictionary customProviders) { } /// public void ResetStatistics() { } /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index c0dd91e2d7..a7b0c5cfd4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -64,9 +64,6 @@ internal bool ForceNewConnection /// private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; - /// Instance-level list of custom key store providers. It can be set more than once by the user. - private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; - // Lock to control setting of s_globalCustomColumnEncryptionKeyStoreProviders private static readonly object s_globalCustomColumnEncryptionKeyProvidersLock = new object(); @@ -164,23 +161,6 @@ static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary - public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) - { - ValidateCustomProviders(customProviders); - - // Create a temporary dictionary and then add items from the provided dictionary. - // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs - // in the provided customerProviders dictionary. - Dictionary customColumnEncryptionKeyStoreProviders = - new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); - - // Set the dictionary to the ReadOnly dictionary. - // This method can be called more than once. Re-registering a new collection will replace the - // old collection of providers. - _customColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; - } - private static void ValidateCustomProviders(IDictionary customProviders) { // Throw when the provided dictionary is null. @@ -233,13 +213,6 @@ static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, return true; } - // instance-level custom provider cache takes precedence over global cache - if (connection._customColumnEncryptionKeyStoreProviders != null && - connection._customColumnEncryptionKeyStoreProviders.Count > 0) - { - return connection._customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); - } - lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false @@ -270,11 +243,6 @@ internal static List GetColumnEncryptionSystemKeyStoreProviders() /// Combined list of provider names internal static List GetColumnEncryptionCustomKeyStoreProviders(SqlConnection connection) { - if (connection._customColumnEncryptionKeyStoreProviders != null && - connection._customColumnEncryptionKeyStoreProviders.Count > 0) - { - return connection._customColumnEncryptionKeyStoreProviders.Keys.ToList(); - } if (s_globalCustomColumnEncryptionKeyStoreProviders != null) { return s_globalCustomColumnEncryptionKeyStoreProviders.Keys.ToList(); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs index d703d4f748..65f875f9ce 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs @@ -22,9 +22,6 @@ public void TestNullDictionary() ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); Assert.Contains(expectedMessage, e.Message); - - e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); - Assert.Contains(expectedMessage, e.Message); } [Fact] @@ -38,9 +35,6 @@ public void TestInvalidProviderName() ArgumentException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); Assert.Contains(expectedMessage, e.Message); - - e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); - Assert.Contains(expectedMessage, e.Message); } [Fact] @@ -54,9 +48,6 @@ public void TestNullProviderValue() ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); Assert.Contains(expectedMessage, e.Message); - - e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); - Assert.Contains(expectedMessage, e.Message); } [Fact] @@ -69,9 +60,6 @@ public void TestEmptyProviderName() ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); Assert.Contains(expectedMessage, e.Message); - - e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); - Assert.Contains(expectedMessage, e.Message); } [Fact] @@ -93,47 +81,5 @@ public void TestCanSetGlobalProvidersOnlyOnce() Utility.ClearSqlConnectionGlobalProviders(); } - - [Fact] - public void TestCanSetInstanceProvidersMoreThanOnce() - { - const string dummyProviderName1 = "DummyProvider1"; - const string dummyProviderName2 = "DummyProvider2"; - const string dummyProviderName3 = "DummyProvider3"; - IDictionary singleKeyStoreProvider = - new Dictionary() - { - {dummyProviderName1, new DummyKeyStoreProvider() } - }; - - IDictionary multipleKeyStoreProviders = - new Dictionary() - { - { dummyProviderName2, new DummyKeyStoreProvider() }, - { dummyProviderName3, new DummyKeyStoreProvider() } - }; - - using (SqlConnection connection = new SqlConnection()) - { - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(singleKeyStoreProvider); - IReadOnlyDictionary instanceCache = - GetInstanceCacheFromConnection(connection); - Assert.Single(instanceCache); - Assert.True(instanceCache.ContainsKey(dummyProviderName1)); - - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(multipleKeyStoreProviders); - instanceCache = GetInstanceCacheFromConnection(connection); - Assert.Equal(2, instanceCache.Count); - Assert.True(instanceCache.ContainsKey(dummyProviderName2)); - Assert.True(instanceCache.ContainsKey(dummyProviderName3)); - } - - IReadOnlyDictionary GetInstanceCacheFromConnection(SqlConnection conn) - { - FieldInfo instanceCacheField = conn.GetType().GetField( - "_customColumnEncryptionKeyStoreProviders", BindingFlags.NonPublic | BindingFlags.Instance); - return instanceCacheField.GetValue(conn) as IReadOnlyDictionary; - } - } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 30e24f8960..b5cc271605 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -2160,28 +2160,6 @@ public void TestCustomKeyStoreProviderDuringAeQuery(string connectionString) () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); Assert.Contains(failedToDecryptMessage, ex.Message); Assert.True(ex.InnerException is NotImplementedException); - - // not required provider in instance cache - // it should not fall back to the global cache so the right provider will not be found - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(notRequiredProvider); - ex = Assert.Throws( - () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); - Assert.Equal(providerNotFoundMessage, ex.Message); - - // required provider in instance cache - // if the instance cache is not empty, it is always checked for the provider. - // => if the provider is found, it must have been retrieved from the instance cache and not the global cache - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(requiredProvider); - ex = Assert.Throws( - () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); - Assert.Contains(failedToDecryptMessage, ex.Message); - Assert.True(ex.InnerException is NotImplementedException); - - // not required provider will replace the previous entry so required provider will not be found - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(notRequiredProvider); - ex = Assert.Throws( - () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); - Assert.Equal(providerNotFoundMessage, ex.Message); } void ExecuteQueryThatRequiresCustomKeyStoreProvider(SqlConnection connection) From d44684fd3d327ea35e7e0c83c0865ee73b9adc69 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Thu, 15 Apr 2021 15:43:11 -0700 Subject: [PATCH 08/87] Fix wrong data blended with transactions in .NET core (#1023) --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 35 +++++++ .../Data/SqlClient/SqlDelegatedTransaction.cs | 7 +- .../SqlClient/SqlInternalConnectionTds.cs | 8 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 7 ++ .../SqlClient/SqlInternalConnectionTds.cs | 6 +- .../SQL/TransactionTest/TransactionTest.cs | 93 +++++++++++++++++++ 6 files changed, 151 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 e7e357ff66..37efae8916 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 @@ -3412,6 +3412,8 @@ private bool TryReadInternal(bool setTimeout, out bool more) { SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent("SqlDataReader.TryReadInternal | API | Object Id {0}", ObjectID); + RuntimeHelpers.PrepareConstrainedRegions(); + try { statistics = SqlStatistics.StartTimer(Statistics); @@ -3561,6 +3563,39 @@ private bool TryReadInternal(bool setTimeout, out bool more) return true; } + catch (OutOfMemoryException e) + { + _isClosed = true; + SqlConnection con = _connection; + if (con != null) + { + con.Abort(e); + } + throw; + } + catch (StackOverflowException e) + { + _isClosed = true; + SqlConnection con = _connection; + if (con != null) + { + con.Abort(e); + } + throw; + } + /* Even though ThreadAbortException exists in .NET Core, + * since Abort is not supported, the common language runtime won't ever throw ThreadAbortException. + * just to keep a common codebase!*/ + catch (System.Threading.ThreadAbortException e) + { + _isClosed = true; + SqlConnection con = _connection; + if (con != null) + { + con.Abort(e); + } + throw; + } finally { SqlStatistics.StopTimer(statistics); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index e3f575bfa8..13257cb827 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -254,8 +254,10 @@ public void Rollback(SinglePhaseEnlistment enlistment) connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); } } - catch (SqlException) + catch (SqlException e) { + ADP.TraceExceptionWithoutRethrow(e); + // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event @@ -273,8 +275,9 @@ public void Rollback(SinglePhaseEnlistment enlistment) // we have the tracing that we're doing to fallback on for the // investigation. } - catch (InvalidOperationException) + catch (InvalidOperationException e) { + ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 6f352799a1..5dcda47e0f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1116,9 +1116,13 @@ bool isDelegateControlRequest stateObj = _parser.GetSession(this); mustPutSession = true; } - else if (internalTransaction.OpenResultsCount != 0) + if (internalTransaction.OpenResultsCount != 0) { - //throw SQL.CannotCompleteDelegatedTransactionWithOpenResults(this); + SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection is marked to be doomed when closed. Transaction ended with OpenResultsCount {1} > 0, MARSOn {2}", + ObjectID, + internalTransaction.OpenResultsCount, + _parser.MARSOn); + throw SQL.CannotCompleteDelegatedTransactionWithOpenResults(this, _parser.MARSOn); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 6c6dd2dddb..8a0f5293b4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -791,6 +791,13 @@ internal static Exception SqlDependencyNoMatchingServerDatabaseStart() // // SQL.SqlDelegatedTransaction // + static internal Exception CannotCompleteDelegatedTransactionWithOpenResults(SqlInternalConnectionTds internalConnection, bool marsOn) + { + SqlErrorCollection errors = new SqlErrorCollection(); + errors.Add(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, null, (StringsHelper.GetString(Strings.ADP_OpenReaderExists, marsOn ? ADP.Command : ADP.Connection)), "", 0, TdsEnums.SNI_WAIT_TIMEOUT)); + return SqlException.CreateException(errors, null, internalConnection); + } + internal static TransactionPromotionException PromotionFailed(Exception inner) { TransactionPromotionException e = new TransactionPromotionException(System.StringsHelper.GetString(Strings.SqlDelegatedTransaction_PromotionFailed), inner); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 4e268a953b..e66081de70 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1380,8 +1380,12 @@ override internal void ExecuteTransaction(TransactionRequest transactionRequest, stateObj = _parser.GetSession(this); mustPutSession = true; } - else if (internalTransaction.OpenResultsCount != 0) + if (internalTransaction.OpenResultsCount != 0) { + SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection is marked to be doomed when closed. Transaction ended with OpenResultsCount {1} > 0, MARSOn {2}", + ObjectID, + internalTransaction.OpenResultsCount, + _parser.MARSOn); throw SQL.CannotCompleteDelegatedTransactionWithOpenResults(this, _parser.MARSOn); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs index b32dd75f46..f5d8240053 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs @@ -10,6 +10,99 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class TransactionTest { + public static TheoryData PoolEnabledConnectionStrings => + new TheoryData + { + new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + { + MultipleActiveResultSets = false, + Pooling = true, + MaxPoolSize = 1 + }.ConnectionString + , new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + { + Pooling = true, + MultipleActiveResultSets = true + }.ConnectionString + }; + + public static TheoryData PoolDisabledConnectionStrings => + new TheoryData + { + new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + { + Pooling = false, + MultipleActiveResultSets = false + }.ConnectionString + , new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + { + Pooling = false, + MultipleActiveResultSets = true + }.ConnectionString + }; + + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [MemberData(nameof(PoolEnabledConnectionStrings))] + public static void ReadNextQueryAfterTxAbortedPoolEnabled(string connString) + => ReadNextQueryAfterTxAbortedTest(connString); + + // Azure SQL has no DTC support + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [MemberData(nameof(PoolDisabledConnectionStrings))] + public static void ReadNextQueryAfterTxAbortedPoolDisabled(string connString) + => ReadNextQueryAfterTxAbortedTest(connString); + + private static void ReadNextQueryAfterTxAbortedTest(string connString) + { + using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope()) + { + using (SqlConnection sqlConnection = new SqlConnection(connString)) + { + SqlCommand cmd = new SqlCommand("SELECT 1", sqlConnection); + sqlConnection.Open(); + var reader = cmd.ExecuteReader(); + // Disposing Transaction Scope before completing read triggers GitHub issue #980 use-case that leads to wrong data in future rounds. + scope.Dispose(); + } + + using (SqlConnection sqlConnection = new SqlConnection(connString)) + using (SqlCommand cmd = new SqlCommand("SELECT 2", sqlConnection)) + { + sqlConnection.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + bool result = reader.Read(); + Assert.True(result); + Assert.Equal(2, reader.GetValue(0)); + } + } + + using (SqlConnection sqlConnection = new SqlConnection(connString)) + using (SqlCommand cmd = new SqlCommand("SELECT 3", sqlConnection)) + { + sqlConnection.Open(); + using (SqlDataReader reader = cmd.ExecuteReaderAsync().Result) + { + bool result = reader.ReadAsync().Result; + Assert.True(result); + Assert.Equal(3, reader.GetValue(0)); + } + } + + using (SqlConnection sqlConnection = new SqlConnection(connString)) + using (SqlCommand cmd = new SqlCommand("SELECT TOP(1) 4 Clm0 FROM sysobjects FOR XML AUTO", sqlConnection)) + { + sqlConnection.Open(); + using (System.Xml.XmlReader reader = cmd.ExecuteXmlReader()) + { + bool result = reader.Read(); + Assert.True(result); + Assert.Equal("4", reader[0]); + } + } + } + } + // Synapse: Enforced unique constraints are not supported. To create an unenforced unique constraint you must include the NOT ENFORCED syntax as part of your statement. [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void TestMain() From 357681cc096d397a74db796a1596291bd5fad598 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Thu, 15 Apr 2021 15:44:57 -0700 Subject: [PATCH 09/87] Remove API to enable event tracing in SNI.dll + update SNI dependency version (#1006) --- .../Interop/SNINativeMethodWrapper.Windows.cs | 18 ------ .../src/Microsoft.Data.SqlClient.csproj | 5 -- .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 - .../Interop/SNINativeManagedWrapperX64.cs | 6 -- .../Interop/SNINativeManagedWrapperX86.cs | 6 -- .../Data/Interop/SNINativeMethodWrapper.cs | 26 -------- .../SqlClient/SqlClientEventSource.Windows.cs | 61 ------------------- tools/props/Versions.props | 4 +- tools/specs/Microsoft.Data.SqlClient.nuspec | 10 +-- 9 files changed, 7 insertions(+), 132 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.Windows.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs index e3b91c6ee5..8201ec41aa 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs @@ -304,12 +304,6 @@ internal struct SNI_Error [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIWriteSyncOverAsync(SNIHandle pConn, [In] SNIPacket pPacket); - - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] - internal static extern bool RegisterTraceProviderWrapper(int eventKeyword); - - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] - internal static extern void UnregisterTraceProviderWrapper(); #endregion internal static uint SniGetConnectionId(SNIHandle pConn, ref Guid connId) @@ -467,18 +461,6 @@ private static void MarshalConsumerInfo(ConsumerInfo consumerInfo, ref Sni_Consu : IntPtr.Zero; native_consumerInfo.ConsumerKey = consumerInfo.key; } - - internal static bool RegisterTraceProvider(int eventKeyword) - { - // Registers the TraceLogging provider, enabling it to generate events. - // Return true if enabled, otherwise false. - return RegisterTraceProviderWrapper(eventKeyword); - } - - internal static void UnregisterTraceProvider() - { - UnregisterTraceProviderWrapper(); - } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index fb55b9172b..4546d7d1ab 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -33,11 +33,6 @@ true - - - Microsoft\Data\SqlClient\SqlClientEventSource.Windows.cs - - Microsoft\Data\SqlClient\SqlClientEventSource.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index e6937ea1c4..d80ca43a2d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -97,9 +97,6 @@ Microsoft\Data\SqlClient\SqlClientEventSource.cs - - Microsoft\Data\SqlClient\SqlClientEventSource.Windows.cs - Microsoft\Data\SqlClient\SqlClientLogger.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs index 3b2549e5de..0cddc32dc1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs @@ -133,11 +133,5 @@ internal static class SNINativeManagedWrapperX64 [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SNIClientCertificateFallbackWrapper(IntPtr pCallbackContext); - - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] - internal static extern bool RegisterTraceProviderWrapper(int eventKeyword); - - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] - internal static extern void UnregisterTraceProviderWrapper(); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs index fc1e90750c..398ecc4872 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs @@ -133,11 +133,5 @@ internal static class SNINativeManagedWrapperX86 [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SNIClientCertificateFallbackWrapper(IntPtr pCallbackContext); - - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] - internal static extern bool RegisterTraceProviderWrapper(int eventKeyword); - - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] - internal static extern void UnregisterTraceProviderWrapper(); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs index a79e0e71e5..0ac874b8b6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs @@ -1075,32 +1075,6 @@ private static void MarshalConsumerInfo(ConsumerInfo consumerInfo, ref Sni_Consu : IntPtr.Zero; native_consumerInfo.ConsumerKey = consumerInfo.key; } - - internal static bool RegisterTraceProvider(int eventKeyword) - { - // Registers the TraceLogging provider, enabling it to generate events. - // Return true if enabled, otherwise false. - if (s_is64bitProcess) - { - return SNINativeManagedWrapperX64.RegisterTraceProviderWrapper(eventKeyword); - } - else - { - return SNINativeManagedWrapperX86.RegisterTraceProviderWrapper(eventKeyword); - } - } - - internal static void UnregisterTraceProvider() - { - if (s_is64bitProcess) - { - SNINativeManagedWrapperX64.UnregisterTraceProviderWrapper(); - } - else - { - SNINativeManagedWrapperX86.UnregisterTraceProviderWrapper(); - } - } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.Windows.cs deleted file mode 100644 index a4b3f0bded..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.Windows.cs +++ /dev/null @@ -1,61 +0,0 @@ -// 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.Diagnostics; -using System.Diagnostics.Tracing; - -namespace Microsoft.Data.SqlClient -{ - internal partial class SqlClientEventSource : SqlClientEventSourceBase - { - private bool _traceLoggingProviderEnabled = false; - - /// - /// Captures application flow traces from native networking implementation - /// - private const EventCommand SNINativeTrace = (EventCommand)8192; - - /// - /// Captures scope trace events from native networking implementation - /// - private const EventCommand SNINativeScope = (EventCommand)16384; - - /// - /// Disables all event tracing in native networking implementation - /// - private const EventCommand SNINativeDisable = (EventCommand)32768; - - protected override void OnEventCommand(EventCommandEventArgs e) - { - base.OnEventCommand(e); - // Internally, EventListener.EnableEvents sends an event command, with a reserved value of 0, -2, or -3. - // When a command is sent via EnableEvents or SendCommand, check if it is a user-defined value - // to enable or disable event tracing in sni.dll. - // If registration fails, all write and unregister commands will be a no-op. - - // If managed networking is enabled, don't call native wrapper methods -#if NETCOREAPP || NETSTANDARD - if (AppContext.TryGetSwitch("Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows", out bool isEnabled) && isEnabled) - { - return; - } -#endif - // Only register the provider if it's not already registered. Registering a provider that is already - // registered can lead to unpredictable behaviour. - if (!_traceLoggingProviderEnabled && e.Command > 0 && (e.Command & (SNINativeTrace | SNINativeScope)) != 0) - { - int eventKeyword = (int)(e.Command & (SNINativeTrace | SNINativeScope)); - _traceLoggingProviderEnabled = SNINativeMethodWrapper.RegisterTraceProvider(eventKeyword); - Debug.Assert(_traceLoggingProviderEnabled, "Failed to enable TraceLogging provider."); - } - else if (_traceLoggingProviderEnabled && (e.Command == SNINativeDisable)) - { - // Only unregister the provider if it's currently registered. - SNINativeMethodWrapper.UnregisterTraceProvider(); - _traceLoggingProviderEnabled = false; - } - } - } -} diff --git a/tools/props/Versions.props b/tools/props/Versions.props index b02ec3f871..feafea25e0 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -11,7 +11,7 @@ - 2.1.1 + 3.0.0-preview1.21104.2 4.3.1 4.3.0 @@ -28,7 +28,7 @@ 4.7.0 - 2.1.1 + 3.0.0-preview1.21104.2 4.7.0 4.7.0 4.7.0 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 13e6d26bdf..b5932d24b9 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -28,14 +28,14 @@ When using NuGet 3.x this package requires at least version 3.4. sqlclient microsoft.data.sqlclient - + - + @@ -48,7 +48,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -61,7 +61,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -74,7 +74,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + From 3a325e9330071bab4e02d01de12f68858c5e19ee Mon Sep 17 00:00:00 2001 From: Javad Date: Thu, 15 Apr 2021 16:45:52 -0700 Subject: [PATCH 10/87] Fix API Spelling: LegacyRowVersionNullBehavior (#1035) Co-authored-by: jJRahnama --- .../src/Microsoft/Data/SqlClient/SqlDataReader.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlDataReader.cs | 2 +- .../Data/SqlClient/LocalAppContextSwitches.cs | 12 ++++++------ .../ManualTests/SQL/DataReaderTest/DataReaderTest.cs | 12 ++++++------ 4 files changed, 14 insertions(+), 14 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 37efae8916..f94f126b80 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 @@ -3771,7 +3771,7 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) { if (columnMetaData.type == SqlDbType.Timestamp) { - if (!LocalAppContextSwitches.LegacyRowVersionNullBehaviour) + if (!LocalAppContextSwitches.LegacyRowVersionNullBehavior) { _data[i].SetToNullOfType(SqlBuffer.StorageType.SqlBinary); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 17aaf3f214..5bbbc07904 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -4283,7 +4283,7 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) { if (columnMetaData.type == SqlDbType.Timestamp) { - if (!LocalAppContextSwitches.LegacyRowVersionNullBehaviour) + if (!LocalAppContextSwitches.LegacyRowVersionNullBehavior) { _data[i].SetToNullOfType(SqlBuffer.StorageType.SqlBinary); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs index ed04cbc606..14084f92b0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs @@ -10,10 +10,10 @@ namespace Microsoft.Data.SqlClient internal static partial class LocalAppContextSwitches { internal const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking"; - internal const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehaviour"; + internal const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"; private static bool _makeReadAsyncBlocking; - private static bool? s_legacyRowVersionNullBehaviour; + private static bool? s_LegacyRowVersionNullBehavior; public static bool MakeReadAsyncBlocking { @@ -29,20 +29,20 @@ public static bool MakeReadAsyncBlocking /// would return an empty byte array. This switch contols whether to preserve that behaviour on newer versions /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned /// - public static bool LegacyRowVersionNullBehaviour + public static bool LegacyRowVersionNullBehavior { get { - if (s_legacyRowVersionNullBehaviour == null) + if (s_LegacyRowVersionNullBehavior == null) { bool value = false; if (AppContext.TryGetSwitch(LegacyRowVersionNullString, out bool providedValue)) { value = providedValue; } - s_legacyRowVersionNullBehaviour = value; + s_LegacyRowVersionNullBehavior = value; } - return s_legacyRowVersionNullBehaviour.Value; + return s_LegacyRowVersionNullBehavior.Value; } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs index 8fa25aaffb..1d1ff574e5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -298,7 +298,7 @@ public static void CheckNullRowVersionIsBDNull() { lock (s_rowVersionLock) { - bool? originalValue = SetLegacyRowVersionNullBehaviour(false); + bool? originalValue = SetLegacyRowVersionNullBehavior(false); try { using (SqlConnection con = new SqlConnection(DataTestUtility.TCPConnectionString)) @@ -318,7 +318,7 @@ public static void CheckNullRowVersionIsBDNull() } finally { - SetLegacyRowVersionNullBehaviour(originalValue); + SetLegacyRowVersionNullBehavior(originalValue); } } } @@ -328,7 +328,7 @@ public static void CheckLegacyNullRowVersionIsEmptyArray() { lock (s_rowVersionLock) { - bool? originalValue = SetLegacyRowVersionNullBehaviour(true); + bool? originalValue = SetLegacyRowVersionNullBehavior(true); try { using (SqlConnection con = new SqlConnection(DataTestUtility.TCPConnectionString)) @@ -351,15 +351,15 @@ public static void CheckLegacyNullRowVersionIsEmptyArray() } finally { - SetLegacyRowVersionNullBehaviour(originalValue); + SetLegacyRowVersionNullBehavior(originalValue); } } } - private static bool? SetLegacyRowVersionNullBehaviour(bool? value) + private static bool? SetLegacyRowVersionNullBehavior(bool? value) { Type switchesType = typeof(SqlCommand).Assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches"); - FieldInfo switchField = switchesType.GetField("s_legacyRowVersionNullBehaviour", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + FieldInfo switchField = switchesType.GetField("s_LegacyRowVersionNullBehavior", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); bool? originalValue = (bool?)switchField.GetValue(null); switchField.SetValue(null, value); return originalValue; From 756190585a26c0743e9587b33113db28a944f101 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 15 Apr 2021 17:52:58 -0700 Subject: [PATCH 11/87] Fix version downgrade (#1036) Azure.Identity references later version causes version downgrade issue. --- tools/props/Versions.props | 2 +- tools/specs/Microsoft.Data.SqlClient.nuspec | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index feafea25e0..8dcbac80fe 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -18,7 +18,7 @@ 1.3.0 - 4.21.1 + 4.22.0 6.8.0 6.8.0 4.5.1 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index b5932d24b9..c03b550e12 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -30,7 +30,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -43,7 +43,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -56,7 +56,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -69,7 +69,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -82,7 +82,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + From 0e861e995bde09d88df3bff3b4db96fc62fbbf24 Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Fri, 16 Apr 2021 03:04:45 +0000 Subject: [PATCH 12/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 18 ------------------ .../netfx/src/Resources/Strings.es.resx | 18 ------------------ .../netfx/src/Resources/Strings.fr.resx | 18 ------------------ .../netfx/src/Resources/Strings.it.resx | 18 ------------------ .../netfx/src/Resources/Strings.ja.resx | 18 ------------------ .../netfx/src/Resources/Strings.ko.resx | 18 ------------------ .../netfx/src/Resources/Strings.pt-BR.resx | 18 ------------------ .../netfx/src/Resources/Strings.ru.resx | 18 ------------------ .../netfx/src/Resources/Strings.zh-Hans.resx | 18 ------------------ .../netfx/src/Resources/Strings.zh-Hant.resx | 18 ------------------ 10 files changed, 180 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index da5159755b..6b5765a690 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -4584,24 +4584,6 @@ "Authentication={0}" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. - - Das Zugriffstoken konnte nicht abgerufen werden. - - - Mit dem Endpunkt der verwalteten Identität kann keine Verbindung hergestellt werden. Stellen Sie sicher, dass Sie eine Azure-Ressource mit eingerichteter Identität ausführen. - - - Es wurde versucht, ein Token mithilfe einer verwalteten Identität abzurufen. - - - Mit dem Instance Metadata Service (IMDS) kann keine Verbindung hergestellt werden. Die Anforderung an den Tokenendpunkt der verwalteten Identität wird übersprungen. - - - Es wurde ein nicht wiederholbarer Fehler empfangen. - - - Fehler nach 5 Wiederholungen. - Unerwarteter Typ beim Deserialisieren erkannt. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index fc93a8eb3e..fb5432e665 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -4584,24 +4584,6 @@ No se puede usar "Authentication={0}" si se ha establecido la propiedad Credential. - - No se pudo adquirir el token de acceso. - - - No se puede conectar con el punto de conexión de la identidad administrada. Compruebe que se está ejecutando en un recurso de Azure que tiene la configuración de identidad. - - - Se intentó obtener el token con la identidad administrada. - - - No se puede conectar con Instance Metadata Service (IMDS). Se omitirá la solicitud al punto de conexión del token de la identidad administrada. - - - Se ha recibido un error no recuperable. - - - Error después de 5 reintentos. - Se ha detectado un tipo inesperado al realizar la deserialización. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index 3ea703dd8e..8ab8d6ecca 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -4584,24 +4584,6 @@ Impossible d'utiliser « Authentication={0} », si la propriété Credential a été définie. - - Impossible d'acquérir le jeton d'accès. - - - Connexion impossible au point de terminaison d'identité managée. Vérifiez que vous exécutez une ressource Azure avec une identité configurée. - - - Tentative d'obtention d'un jeton à l'aide d'une identité managée. - - - Connexion impossible à Instance Metadata Service (IMDS). Demande ignorée pour le point de terminaison de jeton d'identité managé. - - - Une erreur irrécupérable a été reçue. - - - Échec après 5 tentatives. - Type inattendu détecté pendant la désérialisation. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index e0dcc33df2..65bf14742d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -4584,24 +4584,6 @@ Non è possibile usare 'Authentication={0}' se è stata impostata la proprietà Credential. - - Non è stato possibile acquisire il token di accesso. - - - Non è possibile connettersi all'endpoint dell'identità gestita. Verificare che sia in esecuzione una risorsa di Azure con configurazione dell'identità. - - - È stato eseguito un tentativo di ottenere un token usando l'identità gestita. - - - Non è possibile connettersi al servizio metadati dell'istanza. La richiesta all'endpoint del token dell'identità gestita verrà ignorata. - - - È stato ricevuto un errore irreversibile. - - - L'operazione non è riuscita dopo 5 tentativi. - Tipo imprevisto rilevato durante la deserializzazione. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index 0a2a429e3c..42a8c9b56a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -4584,24 +4584,6 @@ Credential プロパティが設定されている場合は、'Authentication={0}' を使用できません。 - - アクセス トークンを取得できませんでした。 - - - マネージド ID エンドポイントに接続できません。ID セットアップがある Azure リソースで実行していることを確認してください。 - - - マネージド ID を使用してトークンを取得しようとしました。 - - - Instance Metadata Service (IMDS) に接続できません。マネージド ID トークンのエンドポイントに対する要求をスキップしています。 - - - 再試行できないエラーを受信しました。 - - - 5 回の再試行後に失敗しました。 - 逆シリアル化で予期しない型が検出されました。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 8e21001134..85c72f105f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -4584,24 +4584,6 @@ 자격 증명 속성이 설정된 경우 'Authentication={0}'을(를) 사용할 수 없습니다. - - 액세스 토큰을 획득할 수 없습니다. - - - 관리 ID 엔드포인트에 연결할 수 없습니다. ID가 설정된 Azure 리소스에서 실행 중인지 확인하세요. - - - 관리 ID를 사용하여 토큰을 가져오려 했습니다. - - - IMDS(Instance Metadata Service)에 연결할 수 없습니다. 관리 ID 토큰 엔드포인트에 대한 요청을 건너뜁니다. - - - 다시 시도할 수 없는 오류가 발생했습니다. - - - 5회 다시 시도한 후에 실패했습니다. - 역직렬화 시 예기치 않은 형식이 검색되었습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index 1a3c634a35..bd242cca25 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -4584,24 +4584,6 @@ Não é possível usar 'Authentication={0}' quando a propriedade Credential está configurada. - - Não foi possível adquirir o token de acesso. - - - Não foi possível se conectar com o ponto de extremidade de Identidade Gerenciada. Verifique se a execução está ocorrendo em um recurso do Azure que tenha configuração de Identidade. - - - Tentativa de obter o token usando a Identidade Gerenciada. - - - Não foi possível se conectar com o IMDS (Serviço de Metadados de Instância). Ignorando a solicitação para o ponto de extremidade do token de Identidade Gerenciada. - - - Um erro sem nova tentativa foi recebido. - - - Falha após cinco novas tentativas. - Tipo inesperado detectado na desserialização. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 980ae262af..5151c1ec6a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -4584,24 +4584,6 @@ Невозможно использовать "Authentication={0}", если задано свойство Credential. - - Не удалось получить токен доступа. - - - Не удалось подключиться к конечной точке Управляемого удостоверения. Убедитесь, что вы используете ресурс Azure, для которого настроено удостоверение. - - - Попытка получить токен с помощью Управляемого удостоверения. - - - Не удается подключиться к Службе метаданных экземпляров (IMDS). Запрос будет передан в конечную точку токена Управляемого удостоверения. - - - Получена невоспроизводимая ошибка. - - - Сбой после 5 попыток. - Обнаружен непредвиденный тип при десериализации. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 37c9553533..fb77f3bf7a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -4584,24 +4584,6 @@ 如果设置了 Credential 属性,则无法使用 "Authentication={0}"。 - - 无法获取访问令牌。 - - - 无法连接到托管标识终结点。请检查你运行的 Azure 资源是否设置了标识。 - - - 已尝试使用托管标识来获取令牌。 - - - 无法连接到实例元数据服务(IMDS)。正在跳过对托管标识令牌终结点的请求。 - - - 收到了不可重试的错误。 - - - 5 次重试后失败。 - 反序列化时检测到意外的类型。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 44828c9e36..0d2aae5c16 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -4584,24 +4584,6 @@ 如果已設定 Credential 屬性,就無法使用 'Authentication={0}'。 - - 無法取得存取權杖。 - - - 無法連線到受控識別端點。請確認您在具有身分識別設定的 Azure 資源上執行。 - - - 嘗試使用受控識別取得權杖。 - - - 無法連線到 Instance Metadata Service (IMDS) 。正在跳過受控識別權杖端點的要求。 - - - 收到無法重試的錯誤。 - - - 重試 5 次後失敗。 - 還原序列化時偵測到未預期的類型。 From f19565a44cacdabe47dc9fa3ecd8297a3cce60bc Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 16 Apr 2021 13:49:18 -0700 Subject: [PATCH 13/87] Fix Azure.Identity version (#1037) --- .../ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs | 2 +- tools/props/Versions.props | 3 +-- ...a.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs index d5ea380f3c..d487b187fb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -388,7 +388,7 @@ public static void ActiveDirectoryManagedIdentityWithInvalidUserIdMustFail(strin AggregateException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); - string expectedMessage = "ManagedIdentityCredential authentication unavailable, the requested identity has not been assigned to this resource."; + string expectedMessage = "ManagedIdentityCredential authentication unavailable"; Assert.Contains(expectedMessage, e.GetBaseException().Message); } diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 8dcbac80fe..ec4365798c 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -45,12 +45,11 @@ - [1.2.2,2.0.0) + [1.6.0,2.0.0) [4.0.3,5.0.0) - 1.1.1 3.1.1 5.2.6 15.9.0 diff --git a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec index 212f2cd735..d063fad949 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -26,17 +26,17 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti - + - + - + From 7ca133cbe46903b965c9d62a2a08a3d0fa02fbe8 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Fri, 16 Apr 2021 16:15:04 -0700 Subject: [PATCH 14/87] Release notes for V3.0.0-preview2 (#1034) --- BUILDGUIDE.md | 6 ++ CHANGELOG.md | 48 +++++++++--- release-notes/3.0/3.0.0-preview2.md | 116 ++++++++++++++++++++++++++++ release-notes/3.0/3.0.md | 1 + release-notes/3.0/README.md | 1 + 5 files changed, 160 insertions(+), 12 deletions(-) create mode 100644 release-notes/3.0/3.0.0-preview2.md diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index d944cd2046..abb3f31e86 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -242,6 +242,12 @@ To use this feature, you must enable the following AppContext switch at applicat **"Switch.Microsoft.Data.SqlClient.EnableRetryLogic"** +## Enabling row version null behavior + +`SqlDataReader` returns a `DBNull` value instead of an empty `byte[]`. To enable the legacy behavior, you must enable the following AppContext switch on application startup: + +**"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"** + ## Debugging SqlClient on Linux from Windows For enhanced developer experience, we support debugging SqlClient on Linux from Windows, using the project "**Microsoft.Data.SqlClient.DockerLinuxTest**" that requires "Container Tools" to be enabled in Visual Studio. You may import configuration: [VS19Components.vsconfig](./tools/vsconfig/VS19Components.vsconfig) if not enabled already. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ef104f47e..e59feab7c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +## [Preview Release 3.0.0-preview2.21106.5] - 2021-04-16 + +### Breaking Changes over preview release v3.0.0-preview1 +- `User Id` connection property now requires `Client Id` instead of `Object Id` for **User-Assigned Managed Identity** [#1010](https://github.com/dotnet/SqlClient/pull/1010) +- `SqlDataReader` now returns a `DBNull` value instead of an empty `byte[]`. Legacy behavior can be enabled by setting `AppContext` switch **Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior** [#998](https://github.com/dotnet/SqlClient/pull/998) + +### Added +- **Microsoft.Data.SqlClient** now depends on **Azure.Identity** library to acquire a token for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. [#1010](https://github.com/dotnet/SqlClient/pull/1010) +- Upgraded Native SNI dependency to **v3.0.0-preview1** along with enhanced event tracing support [#1006](https://github.com/dotnet/SqlClient/pull/1006) + +### Fixed +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set[#1023](https://github.com/dotnet/SqlClient/pull/1023) +- Fixed derived parameters containing incorrect typename [#1020](https://github.com/dotnet/SqlClient/pull/1020) +- Fixed server connection leak possibilities when an exception occurs in pooling layer [#890](https://github.com/dotnet/SqlClient/pull/890) +- Fixed IP connection resolving logic in .NET Core [#1016](https://github.com/dotnet/SqlClient/pull/1016) [#1031](https://github.com/dotnet/SqlClient/pull/1031) + +### Changed +- Performance improvements in `SqlDateTime` to `DateTime` internal conversion method [#912](https://github.com/dotnet/SqlClient/pull/912) +- Improved memory allocation by avoiding unnecessary context switching [1008](https://github.com/dotnet/SqlClient/pull/1008) +- Updated `Microsoft.Identity.Client` version from **4.21.1** to **4.22.0** [#1036](https://github.com/dotnet/SqlClient/pull/1036) +- Various performance improvements [#963](https://github.com/dotnet/SqlClient/pull/963) [#996](https://github.com/dotnet/SqlClient/pull/996) [#1004](https://github.com/dotnet/SqlClient/pull/1004) [#1012](https://github.com/dotnet/SqlClient/pull/1012) [#1017](https://github.com/dotnet/SqlClient/pull/1017) +- Event source tracing improvements [#1018](https://github.com/dotnet/SqlClient/pull/1018) +- Changes to share common files between NetFx and NetCore source code [#871](https://github.com/dotnet/SqlClient/pull/871) [#887](https://github.com/dotnet/SqlClient/pull/887) + ## [Preview Release 3.0.0-preview1.21075.2] - 2021-03-15 ### Breaking Changes over stable release v2.1 @@ -27,7 +51,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed missing error messages in Managed SNI [#882](https://github.com/dotnet/SqlClient/pull/882) - Fixed event source trace string issue [#940](https://github.com/dotnet/SqlClient/pull/940) -### Changes +### Changed - Changed App Context switch `MakeReadAsyncBlocking` default to `false` [#937](https://github.com/dotnet/SqlClient/pull/937) - Replaced usage of `BinaryFormatter` with `DataContractSerializer` [#869](https://github.com/dotnet/SqlClient/pull/869) - Prohibited `DtdProcessing` on `XmlTextReader` instance in .NET Core [#884](https://github.com/dotnet/SqlClient/pull/884) @@ -80,7 +104,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed Bulk Copy Async deadlock issues with custom `IDataReader` when using `SqlDataReader` internally [#779](https://github.com/dotnet/SqlClient/pull/779) - Fixed a serialization issue with `SqlException` in .NET Core [#780](https://github.com/dotnet/SqlClient/pull/780) -### Changes +### Changed - Updated versions of `Microsoft.IdentityModel` package dependencies [#794](https://github.com/dotnet/SqlClient/pull/794) @@ -96,7 +120,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed unobserved exception issue when a timeout occurs before a faulted task completes with an exception [#688](https://github.com/dotnet/SqlClient/pull/688) [#773](https://github.com/dotnet/SqlClient/pull/773) - Fixed an issue where driver continues to prompt for credentials when using Azure Active Directory authentication [#770](https://github.com/dotnet/SqlClient/pull/770) -### Changes +### Changed - Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v2.1.1` and removed symbols from `Microsoft.Data.SqlClient.SNI.runtime`, which are now published to Microsoft Symbols Server [#764](https://github.com/dotnet/SqlClient/pull/764) - Updated `Microsoft.Identity.Client` dependency version to `v4.21.1` [#765](https://github.com/dotnet/SqlClient/pull/765) - Performance improvements when establishing an encrypted channel by removing sync over async method calls [#541](https://github.com/dotnet/SqlClient/pull/541) @@ -136,7 +160,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed SPN generation issue when no port is provided [#629](https://github.com/dotnet/SqlClient/pull/629) - Fixed missing null checks for `SqlErrors` in `SqlException` for .NET Framework implementation [#698](https://github.com/dotnet/SqlClient/pull/698) -### Changes +### Changed - Performance improvements by fixing unnecessary allocations with EventSource implementation [#684](https://github.com/dotnet/SqlClient/pull/684) - Reverted changes to return empty DataTable from GetSchemaTable to return null as before. [#696](https://github.com/dotnet/SqlClient/pull/696) - Removed multiple `CacheConnectionStringProperties` calls when setting `ConnectionString` properties [#683](https://github.com/dotnet/SqlClient/pull/683) @@ -164,7 +188,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed Object null reference issue when failover partner is set [#588](https://github.com/dotnet/SqlClient/pull/588) - Fixed `applicationintent` connection string property issue [#585](https://github.com/dotnet/SqlClient/pull/585) -### Changes +### Changed - Raise warning message when insecure TLS protocols are in use [#591](https://github.com/dotnet/SqlClient/pull/591) ### Breaking Changes @@ -184,7 +208,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed unsafe cast in `SqlException` for `SerializationEntry.Value` - Fixed null reference exceptions in `SqlDelegatedTransaction` methods [#563](https://github.com/dotnet/SqlClient/pull/563) -### Changes +### Changed - Standardized connection string properties for enhanced user experience [#534](https://github.com/dotnet/SqlClient/pull/534) - Improved performance by reducing eventsource tracing related to allocations from TVP write methods [#557](https://github.com/dotnet/SqlClient/pull/557) [#564](https://github.com/dotnet/SqlClient/pull/564) @@ -213,7 +237,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed wrong application domain selected when starting `SqlDependencyListener` [#410](https://github.com/dotnet/SqlClient/pull/410) - Added missing refs for `RowCopied` property in `SqlBulkCopy` [#508](https://github.com/dotnet/SqlClient/pull/508) -### Changes +### Changed - Improved performance by removing unwanted method calls in Event Source tracing [#506](https://github.com/dotnet/SqlClient/pull/506) - Removed Diagnostic Source and Configuration Manager dependencies from .NET Standard implementation [#535](https://github.com/dotnet/SqlClient/pull/535) - Removed redundant calls to `DbConnectionPoolKey.GetType()` [#512](https://github.com/dotnet/SqlClient/pull/512) @@ -252,7 +276,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed concurrent connection speed issues when connecting with Azure Active Directory Authentication modes in .NET Core [#466](https://github.com/dotnet/SqlClient/pull/466) - Fixed issues with `Password` persistence in Connection String [#453](https://github.com/dotnet/SqlClient/pull/453) -### Changes +### Changed - Updated all driver assemblies to be CLS Compliant [#396](https://github.com/dotnet/SqlClient/pull/396) - Updated Bulk Copy error messages to also include Column, Row and non-encrypted Data information [#437](https://github.com/dotnet/SqlClient/pull/437) - Updated error messages for "Always Encrypted - Secure Enclaves" to handle 'Attestation Protocol' and fixed typos [#421](https://github.com/dotnet/SqlClient/pull/421) [#397](https://github.com/dotnet/SqlClient/pull/397) @@ -288,7 +312,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed `ConnectionTime` and `ClientConnectionId` reported by `SqlStatistics` when connection is closed [#341](https://github.com/dotnet/SqlClient/pull/341) - Fixed deadlock issues by reverting async changes to `SNIPacket` [#349](https://github.com/dotnet/SqlClient/pull/349) -### Changes +### Changed - Improved performance of Managed SNI by removing double fetch of domain name [#366](https://github.com/dotnet/SqlClient/pull/366) - Improved performance of Async Method Allocations in Managed SNI [#328](https://github.com/dotnet/SqlClient/pull/328) - Improved performance of Managed SNI by enhancing utilization of resources [#173](https://github.com/dotnet/SqlClient/pull/173) - Ported [dotnet/corefx#35363](https://github.com/dotnet/corefx/pull/35363) and [dotnet/corefx#40732](https://github.com/dotnet/corefx/pull/40732) @@ -310,7 +334,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed driver behavior to abort connection when encountering `SqlException` on `SqlTransaction.Commit` [#299](https://github.com/dotnet/SqlClient/pull/299) - Fixed driver behavior to not throw exception on invalid *app.config* files [#319](https://github.com/dotnet/SqlClient/pull/319) -### Changes +### Changed - Improved async read performance by adding multi-packet target buffer caching [#285](https://github.com/dotnet/SqlClient/pull/285) - Improved performance of `TdsParserStateObject` and `SqlDataReader` snapshot mechanisms [#198](https://github.com/dotnet/SqlClient/pull/198) - Updated `SqlDataReader.Close` documentation [#314](https://github.com/dotnet/SqlClient/pull/314) @@ -332,7 +356,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Exception message grammar: "An SqlParameter [...] is not contained by this `SqlParameterCollection`" [#159](https://github.com/dotnet/SqlClient/issues/159) - Fixing incorrect event id and opcode for the `SqlEventSource` [#241](https://github.com/dotnet/SqlClient/pull/241) -### Changes +### Changed - Update dependency to Microsoft.Data.SqlClient.SNI v1.1.0 [#276](https://github.com/dotnet/SqlClient/pull/276) - Correct timeout remarks for async command methods [#264](https://github.com/dotnet/SqlClient/pull/264) - Improve `SqlBulkCopy` truncation error message [#256](https://github.com/dotnet/SqlClient/issues/256) @@ -347,7 +371,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added `SqlFileStream` support for .NET Framework with `Microsoft.Data.SqlTypes.SqlFileStream` class introduced. [#210](https://github.com/dotnet/SqlClient/pull/210) - Added support for Visual Studio Intellisense with XML Documentation. [#210](https://github.com/dotnet/SqlClient/pull/210) -### Changes +### Changed - Synchronized ref definitions with driver classes. [#180](https://github.com/dotnet/SqlClient/pull/180) - Updated `SNINativeMethodWrapper` to provide the underlying error in the inner exception when we fail to load SNI.dll. [#225](https://github.com/dotnet/SqlClient/pull/225) - Added .editorconfig file and set formatting rules. [#193](https://github.com/dotnet/SqlClient/pull/193) diff --git a/release-notes/3.0/3.0.0-preview2.md b/release-notes/3.0/3.0.0-preview2.md new file mode 100644 index 0000000000..fe433309b9 --- /dev/null +++ b/release-notes/3.0/3.0.0-preview2.md @@ -0,0 +1,116 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.0.0-preview2.21106.5 released 16 April 2021 + +This update brings the below changes over the previous release: + +### Breaking Changes over preview release V3.0.0-preview1 +- `User Id` connection property now requires `Client Id` instead of `Object Id` for **User-Assigned Managed Identity** [#1010](https://github.com/dotnet/SqlClient/pull/1010) [Read more](#azure-identity-dependency-introduction) +- `SqlDataReader` now returns a `DBNull` value instead of an empty `byte[]`. Legacy behavior can be enabled by setting `AppContext` switch **Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior** [#998](https://github.com/dotnet/SqlClient/pull/998) [Read more](#enabling-row-version-null-behavior) + +### Added +**Microsoft.Data.SqlClient** now depends on **Azure.Identity** library to acquire a token for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. [#1010](https://github.com/dotnet/SqlClient/pull/1010) [Read more](#azure-identity-dependency-introduction) +- Upgraded Native SNI dependency to **v3.0.0-preview1** along with enhanced event tracing support [#1006](https://github.com/dotnet/SqlClient/pull/1006) [Read more](#event-tracing-improvements-in-sni.dll) + +### Fixed +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set [#1023](https://github.com/dotnet/SqlClient/pull/1023) +- Fixed derived parameters containing incorrect typename [#1020](https://github.com/dotnet/SqlClient/pull/1020) +- Fixed server connection leak possibilities when an exception occurs in pooling layer [#890](https://github.com/dotnet/SqlClient/pull/890) +- Fixed IP connection resolving logic in .NET Core [#1016](https://github.com/dotnet/SqlClient/pull/1016) [#1031](https://github.com/dotnet/SqlClient/pull/1031) + +### Changed +- Performance improvements in `SqlDateTime` to `DateTime` internal conversion method [#912](https://github.com/dotnet/SqlClient/pull/912) +- Improved memory allocation by avoiding unnecessary context switching [1008](https://github.com/dotnet/SqlClient/pull/1008) +- Updated `Microsoft.Identity.Client` version from **4.21.1** to **4.22.0** [#1036](https://github.com/dotnet/SqlClient/pull/1036) +- Various performance improvements [#963](https://github.com/dotnet/SqlClient/pull/963) [#996](https://github.com/dotnet/SqlClient/pull/996) [#1004](https://github.com/dotnet/SqlClient/pull/1004) [#1012](https://github.com/dotnet/SqlClient/pull/1012) [#1017](https://github.com/dotnet/SqlClient/pull/1017) +- Event source tracing improvements [#1018](https://github.com/dotnet/SqlClient/pull/1018) +- Changes to share common files between NetFx and NetCore source code [#871](https://github.com/dotnet/SqlClient/pull/871) [#887](https://github.com/dotnet/SqlClient/pull/887) + +### Azure Identity dependency introduction +**Microsoft.Data.SqlClient** now depends on **Azure.Identity** library underneath to acquire token for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. This change brings the following changes to public surface area: + +- **Breaking Change** + "User Id" connection property now requires "Client Id" instead of "Object Id" for "User-Assigned Managed Identity". +- **Public API** + New read-only public property: `SqlAuthenticationParameters.ConnectionTimeout` +- **Dependency** + Azure.Identity v1.3.0 + +### Event tracing improvements in SNI.dll +`Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) versions have been updated to `v3.0.0-preview1.21104.2`. Event tracing in SNI.dll will no longer be enabled through a client application. Subscribing a session to the **Microsoft.Data.SqlClient.EventSource** provider through tools like xperf or perfview will be sufficient. + +### Enabling row version null behavior +`SqlDataReader` returns a `DBNull` value instead of an empty `byte[]`. To enable the legacy behavior, you must enable the following AppContext switch on application startup: +**"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"** + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework 4.6.1 + +- Microsoft.Data.SqlClient.SNI 3.0.0-preview1.21104.2 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/3.0/3.0.md b/release-notes/3.0/3.0.md index 8dfa0288b2..7dff6ce7de 100644 --- a/release-notes/3.0/3.0.md +++ b/release-notes/3.0/3.0.md @@ -4,4 +4,5 @@ The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2021/04/15 | 3.0.0-preview2.21106.5 | [release notes](3.0.0-preview2.md) | | 2021/03/15 | 3.0.0-preview1.21075.2 | [release notes](3.0.0-preview1.md) | diff --git a/release-notes/3.0/README.md b/release-notes/3.0/README.md index 8dfa0288b2..7dff6ce7de 100644 --- a/release-notes/3.0/README.md +++ b/release-notes/3.0/README.md @@ -4,4 +4,5 @@ The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2021/04/15 | 3.0.0-preview2.21106.5 | [release notes](3.0.0-preview2.md) | | 2021/03/15 | 3.0.0-preview1.21075.2 | [release notes](3.0.0-preview1.md) | From 1d4522a2eddb69cfac71d98817db012864b46260 Mon Sep 17 00:00:00 2001 From: David Engel Date: Fri, 16 Apr 2021 16:25:21 -0700 Subject: [PATCH 15/87] Grammar fixes --- release-notes/3.0/3.0.0-preview2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release-notes/3.0/3.0.0-preview2.md b/release-notes/3.0/3.0.0-preview2.md index fe433309b9..df52852bd7 100644 --- a/release-notes/3.0/3.0.0-preview2.md +++ b/release-notes/3.0/3.0.0-preview2.md @@ -27,10 +27,10 @@ This update brings the below changes over the previous release: - Changes to share common files between NetFx and NetCore source code [#871](https://github.com/dotnet/SqlClient/pull/871) [#887](https://github.com/dotnet/SqlClient/pull/887) ### Azure Identity dependency introduction -**Microsoft.Data.SqlClient** now depends on **Azure.Identity** library underneath to acquire token for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. This change brings the following changes to public surface area: +**Microsoft.Data.SqlClient** now depends on the **Azure.Identity** library to acquire tokens for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. This change brings the following changes to the public surface area: - **Breaking Change** - "User Id" connection property now requires "Client Id" instead of "Object Id" for "User-Assigned Managed Identity". + The "User Id" connection property now requires "Client Id" instead of "Object Id" for "User-Assigned Managed Identity". - **Public API** New read-only public property: `SqlAuthenticationParameters.ConnectionTimeout` - **Dependency** From c48b6d748ceeb07418a21d3b30b3ff287742c986 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 23 Apr 2021 14:13:11 -0700 Subject: [PATCH 16/87] Fix | Fix race condition issues between SinglePhaseCommit and TransactionEnded events (#1042) --- .../Data/SqlClient/SqlDelegatedTransaction.cs | 81 +++++++++---------- .../Data/SqlClient/SqlDelegatedTransaction.cs | 81 +++++++++---------- 2 files changed, 78 insertions(+), 84 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index 13257cb827..e861c7ec31 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -324,24 +324,21 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) RuntimeHelpers.PrepareConstrainedRegions(); try { - // If the connection is doomed, we can be certain that the - // transaction will eventually be rolled back, and we shouldn't - // attempt to commit it. - if (connection.IsConnectionDoomed) + lock (connection) { - lock (connection) + // If the connection is doomed, we can be certain that the + // transaction will eventually be rolled back or has already been aborted externally, and we shouldn't + // attempt to commit it. + if (connection.IsConnectionDoomed) { _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; - } - enlistment.Aborted(SQL.ConnectionDoomed()); - } - else - { - Exception commitException; - lock (connection) + enlistment.Aborted(SQL.ConnectionDoomed()); + } + else { + Exception commitException; try { // Now that we've acquired the lock, make sure we still have valid state for this operation. @@ -367,40 +364,40 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) commitException = e; connection.DoomThisConnection(); } - } - if (commitException != null) - { - // connection.ExecuteTransaction failed with exception - if (_internalTransaction.IsCommitted) - { - // Even though we got an exception, the transaction - // was committed by the server. - enlistment.Committed(); - } - else if (_internalTransaction.IsAborted) + if (commitException != null) { - // The transaction was aborted, report that to - // SysTx. - enlistment.Aborted(commitException); + // connection.ExecuteTransaction failed with exception + if (_internalTransaction.IsCommitted) + { + // Even though we got an exception, the transaction + // was committed by the server. + enlistment.Committed(); + } + else if (_internalTransaction.IsAborted) + { + // The transaction was aborted, report that to + // SysTx. + enlistment.Aborted(commitException); + } + else + { + // The transaction is still active, we cannot + // know the state of the transaction. + enlistment.InDoubt(commitException); + } + + // We eat the exception. This is called on the SysTx + // thread, not the applications thread. If we don't + // eat the exception an UnhandledException will occur, + // causing the process to FailFast. } - else + + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + if (commitException == null) { - // The transaction is still active, we cannot - // know the state of the transaction. - enlistment.InDoubt(commitException); + // connection.ExecuteTransaction succeeded + enlistment.Committed(); } - - // We eat the exception. This is called on the SysTx - // thread, not the applications thread. If we don't - // eat the exception an UnhandledException will occur, - // causing the process to FailFast. - } - - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); - if (commitException == null) - { - // connection.ExecuteTransaction succeeded - enlistment.Committed(); } } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index 082de06d3f..7bf191e837 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -391,24 +391,21 @@ public void SinglePhaseCommit(SysTx.SinglePhaseEnlistment enlistment) #else { #endif //DEBUG - // If the connection is doomed, we can be certain that the - // transaction will eventually be rolled back, and we shouldn't - // attempt to commit it. - if (connection.IsConnectionDoomed) + lock (connection) { - lock (connection) + // If the connection is doomed, we can be certain that the + // transaction will eventually be rolled back or has already been aborted externally, and we shouldn't + // attempt to commit it. + if (connection.IsConnectionDoomed) { _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; - } - enlistment.Aborted(SQL.ConnectionDoomed()); - } - else - { - Exception commitException; - lock (connection) + enlistment.Aborted(SQL.ConnectionDoomed()); + } + else { + Exception commitException; try { // Now that we've acquired the lock, make sure we still have valid state for this operation. @@ -437,40 +434,40 @@ public void SinglePhaseCommit(SysTx.SinglePhaseEnlistment enlistment) ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } - } - if (commitException != null) - { - // connection.ExecuteTransaction failed with exception - if (_internalTransaction.IsCommitted) - { - // Even though we got an exception, the transaction - // was committed by the server. - enlistment.Committed(); - } - else if (_internalTransaction.IsAborted) + if (commitException != null) { - // The transaction was aborted, report that to - // SysTx. - enlistment.Aborted(commitException); + // connection.ExecuteTransaction failed with exception + if (_internalTransaction.IsCommitted) + { + // Even though we got an exception, the transaction + // was committed by the server. + enlistment.Committed(); + } + else if (_internalTransaction.IsAborted) + { + // The transaction was aborted, report that to + // SysTx. + enlistment.Aborted(commitException); + } + else + { + // The transaction is still active, we cannot + // know the state of the transaction. + enlistment.InDoubt(commitException); + } + + // We eat the exception. This is called on the SysTx + // thread, not the applications thread. If we don't + // eat the exception an UnhandledException will occur, + // causing the process to FailFast. } - else + + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + if (commitException == null) { - // The transaction is still active, we cannot - // know the state of the transaction. - enlistment.InDoubt(commitException); + // connection.ExecuteTransaction succeeded + enlistment.Committed(); } - - // We eat the exception. This is called on the SysTx - // thread, not the applications thread. If we don't - // eat the exception an UnhandledException will occur, - // causing the process to FailFast. - } - - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); - if (commitException == null) - { - // connection.ExecuteTransaction succeeded - enlistment.Committed(); } } } From 5f171ae9872bef69f79b65120e728acb33cadee2 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Mon, 26 Apr 2021 14:07:34 -0700 Subject: [PATCH 17/87] Tests | Fix TvpTest intermittent failures (#1046) Co-authored-by: Davoud Eshtehari --- .../SQL/ParameterTest/StreamInputParam.cs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/StreamInputParam.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/StreamInputParam.cs index 65e32cca90..3d15b4346b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/StreamInputParam.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/StreamInputParam.cs @@ -457,17 +457,16 @@ private static void ImmediateCancelBin() byte[] data = new byte[dataSize]; rand.NextBytes(data); MemoryStream ms = new MemoryStream(data, false); - cmd.CommandText = "insert into #blobs (Id, blob) values (1, @blob)"; + // Include a delay to allow time for cancellation + cmd.CommandText = "WAITFOR DELAY '00:00:05'; insert into #blobs (Id, blob) values (1, @blob)"; cmd.Parameters.Add("@blob", SqlDbType.VarBinary, dataSize); cmd.Parameters["@blob"].Direction = ParameterDirection.Input; cmd.Parameters["@blob"].Value = ms; - Task t = cmd.ExecuteNonQueryAsync(cts.Token); - if (!t.IsCompleted) - cts.Cancel(); + try { - t.Wait(); - Console.WriteLine("FAIL: Expected AggregateException on Task wait for Cancelled Task! Task Status: " + t.Status); + Task.WaitAll(cmd.ExecuteNonQueryAsync(cts.Token), Task.Run(() => cts.Cancel())); + Console.WriteLine("FAIL: Expected AggregateException on Task wait for Cancelled Task!"); } catch (AggregateException ae) { @@ -504,17 +503,16 @@ private static void ImmediateCancelText() StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000000; i++) sb.Append(i); - cmd.CommandText = "insert into #blobs (Id, blob) values (1, @blob)"; + // Include a delay to allow time for cancellation + cmd.CommandText = "WAITFOR DELAY '00:00:05'; insert into #blobs (Id, blob) values (1, @blob)"; cmd.Parameters.Add("@blob", SqlDbType.VarChar, -1); cmd.Parameters["@blob"].Direction = ParameterDirection.Input; cmd.Parameters["@blob"].Value = new StringReader(sb.ToString()); - Task t = cmd.ExecuteNonQueryAsync(cts.Token); - if (!t.IsCompleted) - cts.Cancel(); + try { - t.Wait(); - Console.WriteLine("FAIL: Expected AggregateException on Task wait for Cancelled Task! Task Status: " + t.Status); + Task.WaitAll(cmd.ExecuteNonQueryAsync(cts.Token), Task.Run(() => cts.Cancel())); + Console.WriteLine("FAIL: Expected AggregateException on Task wait for Cancelled Task!"); } catch (AggregateException ae) { @@ -544,17 +542,16 @@ private static void ImmediateCancelXml() { cmd.CommandText = "create table #blobs (Id int, blob xml)"; cmd.ExecuteNonQuery(); - cmd.CommandText = "insert into #blobs (Id, blob) values (1, @blob)"; + // Include a delay to allow time for cancellation + cmd.CommandText = "WAITFOR DELAY '00:00:05'; insert into #blobs (Id, blob) values (1, @blob)"; cmd.Parameters.Add("@blob", SqlDbType.Xml, -1); cmd.Parameters["@blob"].Direction = ParameterDirection.Input; cmd.Parameters["@blob"].Value = XmlReader.Create(new StringReader(XmlStr)); - Task t = cmd.ExecuteNonQueryAsync(cts.Token); - if (!t.IsCompleted) - cts.Cancel(); + try { - t.Wait(); - Console.WriteLine("FAIL: Expected AggregateException on Task wait for Cancelled Task! Task Status: " + t.Status); + Task.WaitAll(cmd.ExecuteNonQueryAsync(cts.Token), Task.Run(() => cts.Cancel())); + Console.WriteLine("FAIL: Expected AggregateException on Task wait for Cancelled Task!"); } catch (AggregateException ae) { From 9b4cf94e0ac256772d35fefc490c15f96c776369 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Wed, 28 Apr 2021 11:15:55 -0700 Subject: [PATCH 18/87] Improve configurable retry logic tests (#1026) --- BUILDGUIDE.md | 123 +++++----- build.proj | 1 + src/Microsoft.Data.SqlClient.sln | 3 + .../CustomConfigurableRetryLogic.cs | 6 +- .../CustomRetryLogicProvider.csproj | 24 ++ .../ManualTests/DataCommon/DataTestUtility.cs | 2 - ....Data.SqlClient.ManualTesting.Tests.csproj | 22 +- ...ssLibrary_CustomConfigurableRetryLogic.dll | Bin 10240 -> 0 bytes ...ssLibrary_CustomConfigurableRetryLogic.dll | Bin 10240 -> 0 bytes .../RetryLogic/SqlCommandReliabilityTest.cs | 214 +++++++++--------- .../SqlConfigurationManagerReliabilityTest.cs | 32 +-- 11 files changed, 230 insertions(+), 197 deletions(-) rename src/Microsoft.Data.SqlClient/tests/{ManualTests/SQL/RetryLogic/Resources => CustomConfigurableRetryLogic}/CustomConfigurableRetryLogic.cs (94%) create mode 100644 src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomRetryLogicProvider.csproj delete mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/Resources/netcoreapp/ClassLibrary_CustomConfigurableRetryLogic.dll delete mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/Resources/netfx/ClassLibrary_CustomConfigurableRetryLogic.dll diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index abb3f31e86..5a7d0080e1 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -21,12 +21,12 @@ Once the environment is setup properly, execute the desired set of commands belo ```bash > msbuild /p:Configuration=Release -# Builds the driver in 'Release' Configuration. +# Builds the driver in 'Release' Configuration for `AnyCPU` platform. ``` ```bash > msbuild /p:Platform=Win32 -# Builds the .NET Framework (NetFx) driver for Win32 (x86) platform on Windows. +# Builds the .NET Framework (NetFx) driver for Win32 (x86) platform on Windows in 'Debug' Configuration. ``` ```bash @@ -64,35 +64,37 @@ Once the environment is setup properly, execute the desired set of commands belo ```bash > msbuild /t:BuildTestsNetCore -# Build the tests for the .NET Core driver. Default .NET Core version is 2.1. +# Build the tests for the .NET Core driver in 'Debug' Configuration. Default .NET Core version is 2.1. ``` ```bash > msbuild /t:BuildTestsNetFx -# Build the tests for the .NET Framework (NetFx) driver. Default .NET Framework version is 4.6. +# Build the tests for the .NET Framework (NetFx) driver in 'Debug' Configuration. Default .NET Framework version is 4.6.1. ``` ## Run Functional Tests -Windows (`netfx x86`): -```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -``` +- Windows (`netfx x86`): + ```bash + > dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" + ``` -Windows (`netfx x64`): -```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -``` +- Windows (`netfx x64`): + ```bash + > dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" + ``` -Windows (`netcoreapp`): -```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -``` +- AnyCPU: -Unix (`netcoreapp`): -```bash -> dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" -``` + Windows (`netcoreapp`): + ```bash + > dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" + ``` + + Unix (`netcoreapp`): + ```bash + > dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" + ``` ## Run Manual Tests @@ -102,46 +104,53 @@ Manual Tests require the below setup to run: * Databases "NORTHWIND" and "UdtTestDb" present in SQL Server, created using SQL scripts [createNorthwindDb.sql](tools/testsql/createNorthwindDb.sql) and [createUdtTestDb.sql](tools/testsql/createUdtTestDb.sql). To setup an Azure Database with "NORTHWIND" tables, use SQL Script: [createNorthwindAzureDb.sql](tools/testsql/createNorthwindAzureDb.sql). * Make a copy of the configuration file [config.default.json](src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json) and rename it to `config.json`. Update the values in `config.json`: -|Property|Description|Value| -|------|--------|-------------------| -|TCPConnectionString | Connection String for a TCP enabled SQL Server instance. | `Server={servername};Database={Database_Name};Trusted_Connection=True;`
OR `Data Source={servername};Initial Catalog={Database_Name};Integrated Security=True;`| -|NPConnectionString | Connection String for a Named Pipes enabled SQL Server instance.| `Server=\\{servername}\pipe\sql\query;Database={Database_Name};Trusted_Connection=True;`
OR
`Data Source=np:{servername};Initial Catalog={Database_Name};Integrated Security=True;`| -|TCPConnectionStringHGSVBS | (Optional) Connection String for a TCP enabled SQL Server with Host Guardian Service (HGS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = HGS; Enclave Attestation Url = {AttestationURL};`| -|AADAuthorityURL | (Optional) Identifies the OAuth2 authority resource for `Server` specified in `AADPasswordConnectionString` | `https://login.windows.net/`, where `` is the tenant ID of the Azure Active Directory (Azure AD) tenant | -|AADPasswordConnectionString | (Optional) Connection String for testing Azure Active Directory Password Authentication. | `Data Source={server.database.windows.net}; Initial Catalog={Azure_DB_Name};Authentication=Active Directory Password; User ID={AAD_User}; Password={AAD_User_Password};`| -|AADSecurePrincipalId | (Optional) The Application Id of a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Application ID} | -|AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} | -|AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` | -|AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ | -|AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ | -|AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ | -|SupportsLocalDb | (Optional) Whether or not a LocalDb instance of SQL Server is installed on the machine running the tests. |`true` OR `false`| -|SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`| -|SupportsFileStream | (Optional) Whether or not FileStream is enabled on SQL Server| `true` OR `false`| -|UseManagedSNIOnWindows | (Optional) Enables testing with Managed SNI on Windows| `true` OR `false`| -|IsAzureSynpase | (Optional) When set to 'true', test suite runs compatible tests for Azure Synapse/Parallel Data Warehouse. | `true` OR `false`| - -Commands to run tests: - -Windows (`netfx x86`): -```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -``` + |Property|Description|Value| + |------|--------|-------------------| + |TCPConnectionString | Connection String for a TCP enabled SQL Server instance. | `Server={servername};Database={Database_Name};Trusted_Connection=True;`
OR `Data Source={servername};Initial Catalog={Database_Name};Integrated Security=True;`| + |NPConnectionString | Connection String for a Named Pipes enabled SQL Server instance.| `Server=\\{servername}\pipe\sql\query;Database={Database_Name};Trusted_Connection=True;`
OR
`Data Source=np:{servername};Initial Catalog={Database_Name};Integrated Security=True;`| + |TCPConnectionStringHGSVBS | (Optional) Connection String for a TCP enabled SQL Server with Host Guardian Service (HGS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = HGS; Enclave Attestation Url = {AttestationURL};`| + |AADAuthorityURL | (Optional) Identifies the OAuth2 authority resource for `Server` specified in `AADPasswordConnectionString` | `https://login.windows.net/`, where `` is the tenant ID of the Azure Active Directory (Azure AD) tenant | + |AADPasswordConnectionString | (Optional) Connection String for testing Azure Active Directory Password Authentication. | `Data Source={server.database.windows.net}; Initial Catalog={Azure_DB_Name};Authentication=Active Directory Password; User ID={AAD_User}; Password={AAD_User_Password};`| + |AADSecurePrincipalId | (Optional) The Application Id of a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Application ID} | + |AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} | + |AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` | + |AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ | + |AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ | + |AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ | + |SupportsLocalDb | (Optional) Whether or not a LocalDb instance of SQL Server is installed on the machine running the tests. |`true` OR `false`| + |SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`| + |SupportsFileStream | (Optional) Whether or not FileStream is enabled on SQL Server| `true` OR `false`| + |UseManagedSNIOnWindows | (Optional) Enables testing with Managed SNI on Windows| `true` OR `false`| + |IsAzureSynpase | (Optional) When set to 'true', test suite runs compatible tests for Azure Synapse/Parallel Data Warehouse. | `true` OR `false`| + +### Commands to run Manual Tests: + + - Windows (`netfx x86`): + ```bash + > dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" + ``` -Windows (`netfx x64`): -```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -``` + - Windows (`netfx x64`): + ```bash + > dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" + ``` -Windows (`netcoreapp`): -```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -``` + - AnyCPU: -Unix (`netcoreapp`): -```bash -> dotnet test "src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" -``` + Windows (`netfx`): + ```bash + > dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" + ``` + + Windows (`netcoreapp`): + ```bash + > dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" + ``` + + Unix (`netcoreapp`): + ```bash + > dotnet test "src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" + ``` ## Run A Single Test ```bash diff --git a/build.proj b/build.proj index 428008a4a8..9d59894bf0 100644 --- a/build.proj +++ b/build.proj @@ -48,6 +48,7 @@ + diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 127672b0f9..a03e196464 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -172,6 +172,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.Te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.ExtUtilities", "Microsoft.Data.SqlClient\tests\tools\Microsoft.Data.SqlClient.ExtUtilities\Microsoft.Data.SqlClient.ExtUtilities.csproj", "{E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomRetryLogicProvider", "Microsoft.Data.SqlClient\tests\CustomConfigurableRetryLogic\CustomRetryLogicProvider.csproj", "{B499E477-C9B1-4087-A5CF-5C762D90E433}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1159,6 +1161,7 @@ Global {E7336BFB-8521-423A-A140-3123F9065C5D} = {0CC4817A-12F3-4357-912C-09315FAAD008} {89D6D382-9B36-43C9-A912-03802FDA8E36} = {0CC4817A-12F3-4357-912C-09315FAAD008} {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B} = {0CC4817A-12F3-4357-912C-09315FAAD008} + {B499E477-C9B1-4087-A5CF-5C762D90E433} = {0CC4817A-12F3-4357-912C-09315FAAD008} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/Resources/CustomConfigurableRetryLogic.cs b/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomConfigurableRetryLogic.cs similarity index 94% rename from src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/Resources/CustomConfigurableRetryLogic.cs rename to src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomConfigurableRetryLogic.cs index 4a1d16bbe7..8908857e4a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/Resources/CustomConfigurableRetryLogic.cs +++ b/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomConfigurableRetryLogic.cs @@ -6,9 +6,9 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.Data.SqlClient; -namespace ClassLibrary +// These types have been created just to test the configurable retry logic manager in the Manual tests project. +namespace Microsoft.Data.SqlClient.Tests { public class CustomConfigurableRetryLogic { @@ -27,8 +27,8 @@ public class CustomConfigurableRetryLogicEx { public SqlRetryLogicBaseProvider GetDefaultRetry(SqlRetryLogicOption option = null) { + // Trying to get access to a provider inside a custom implementation. SqlRetryLogicBaseProvider provider = new SqlCommand().RetryLogicProvider; - Console.WriteLine(provider.RetryLogic.NumberOfTries); return new CustomRetryLogicProvider(option?.NumberOfTries ?? 1); } } diff --git a/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomRetryLogicProvider.csproj b/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomRetryLogicProvider.csproj new file mode 100644 index 0000000000..16d5397566 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomRetryLogicProvider.csproj @@ -0,0 +1,24 @@ + + + ExternalConfigurableRetryLogic + netfx + netcoreapp + $(OS) + true + true + false + Debug;Release;net461-Release;net461-Debug;netcoreapp2.1-Debug;netcoreapp2.1-Release;netcoreapp3.1-Debug;netcoreapp3.1-Release + AnyCPU;x86;x64 + $(ObjFolder)$(Configuration).$(Platform).$(AssemblyName) + $(BinFolder)$(Configuration).$(Platform).$(AssemblyName) + + + + + + + + + + + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 58dd79df04..a97a16a05d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -15,8 +15,6 @@ using Microsoft.Identity.Client; using Microsoft.Data.SqlClient.TestUtilities; using Xunit; -using Azure.Security.KeyVault.Keys; -using Azure.Identity; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { 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 def31ffc10..0878e72f74 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 @@ -70,8 +70,10 @@ Common\System\Collections\DictionaryExtensions.cs - + + + @@ -195,9 +197,6 @@ - - - @@ -292,6 +291,7 @@ + @@ -313,18 +313,4 @@ - - - - PreserveNewest - ClassLibrary_CustomConfigurableRetryLogic.dll - - - - - - PreserveNewest - ClassLibrary_CustomConfigurableRetryLogic.dll - - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/Resources/netcoreapp/ClassLibrary_CustomConfigurableRetryLogic.dll b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/Resources/netcoreapp/ClassLibrary_CustomConfigurableRetryLogic.dll deleted file mode 100644 index fcc2a8ee94c395cf9069be2838082011336caa9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeHMYj7Labv}0iEJ*Mn0Te0AqABP>8`c9RB~p=Oy(m5;%A}}}lx-#QKp?QBU;|ia zcOi+U5L4s&F{v8IKN?S_nTf30X*C*!(~J&h7i+GN^k9jEEE zbtabjowJJvCCcNZ^^Z)mMyF1Z?2v^k+Gu%%bc;2xo9ex&znwk%7|LUd^D4f4vbAib7tCzH#LRU zd8!W&5{)PyZG0sF>+bb^S`%$hT|^zg>s;Eu>_i*Iy%#spQt4~1-%Mct<(dZsoi869 ze~4N6zuI~zlW;u&y<^#Qd?L|k&9mQR9?S=Po%gCBwWMAnxZq{`(?wV^a(Y_i~ zx?ktG;$q)43edBiMBiv7QbPU3!Sfa*3o%4IWF0}z4x`s;MgYwgfb&SaiY-IWM%n4> zJG2j^T211rJK!>QYp&UIEbN zSnxbNP_Y{^ejYPYv6bkVh_%PQYSXIC_DyBldZLwGI(-QCp*L)!?n0rqh7BJizqtzi z#!Dxf^m;Q|5x43!D%|*62)62Mt1)=7b9wMQw5f}oH&l&Z>|9ZQF}9kmtpT`1!R2>1 z1XqPNUvPal*Mh3sh)n4II+m7tZJ_#PG>Vw18*NM>bidae)HeB+Z_u?EvKz9Oa1amr zgygFsmE-yu`xdapVgTl?44^+0+W>HVnZ+!;E2BHSboolit$+Q>E{%Gwj_D1Rn0Az7 z>IFobW2w#Gt3;wE4k3<13-s-QKIAtXq1Z-DqO=JhLRu*jdq>w-zvwFSfy(&=Yi&C>cyP13fO8l*kz4#!}fbukv}6OU zR?tvo??cm|Z(OcPHI=pj)!Iti(QNxL=0N(!UE16ZpeA;fkcnj&YKr(`yU=dhqqTL0 zN~}n$FYV^$Ean}G__P@O-SFKfIS6w+(4?K^=5lL2N?2?4q@bq{)W%E4Td-8vbl5M| zdkJFfLQ6mk2bSM0?zoho1b>fl<;oRT!Y#)ZU0Ze8^<9`CDTFH-`@e21nv-cm%uLzd`08Y*Yv0SA-WKF zTMtnPu$g`ycnuoif575-bM_Fo@0Or^50TA8hnAeE7&7}7pNDQPsYa8 zNU$6IpkSMVE*7G5*iLWt#u}*}*a)zDz}Y06j|4`7y}*``4`ZCqdyu&n!G2G$25P0% zv`l|at)|!2^GeYv{Rf!e6B@&B2fhxd0|x3V8?^@dj*E$fM*4ouSR*yAVGFMHCR!$#tEGua>E+taGtl+Dc%LdKi!0NEbS* zV>i;$rHbyvvQl&jP(yrc`%9Pl=uiFEQe3HbhAed&Rrrf(X8uvlh4qxfZjqyUYS$I^ z49?^Vz!07e?fO#GGQ;fx`veXPe80e>0>1{>O5X<5=~dzgyhQ*o5`)ND);ScnT;VjLz zmeIc}i`wZk>H*<-2s{_Tvt7-=%29Bhrwkx=5K8M$&`Gzi3W48&tvBfc%_^p|C!eBq zbV~aYZIsorQL2BVteK6nHr7#F@Q2{Z`TvWQI;91aQcO`Q=U=LNgr`TOdSrF=$m;45 z4Lilp0s2W`SdGxv)KT#Bs_PM3lk_>21^u6O3-2+*frr&3{ZxMdeU9NVk^L-WFQ~Uf zRw?Fx3;bBAiusjd&Ioq&FQ^Ez$FNi2tpYa*>=xK7aJ#@=0*?qx0><%tXwl;EtsQVB z-43{x1^_qE{eWBO4*+kcKL+fl7Xa@N`UK!wm7)<}zrd*WLHb*5FJQB8UYi5`D+0OV z2)o}AdIsHAbzare&9GBXF}ev=xE}owGH?^6=sr44pQpd1n0lYur^c0`KCb-pt_EIP z?sWx?pI4NJe_Dl6*Cfxezd3|OC6~KTFXa1_?A?AyR%IVsgf=8f7V^C%A`A81gEh^* z)av_+=&Q$*HRQc4*VF4lYw+5L*J9Te`dYLpdIQzjpw7{0^*BB4y0_nO28`)sG3$&Q zjy2oeMLTyF6NzrxIe5lM6&<6`p3SFrrxS@S$Gd5$m`|Y@9?TbWhLxPk8o(x#_I*t3 zH?vtIfZ z2@o>eZn@T(9@;r-ri)o)H}z+ewmp)WvXa(pqQ7W6X0CsHgeGQf$H>Kp$I88dq?4o> z!%4_QN6eW_YEfA;Kb@H=%H(P|C!C~{NqOq8$6^&z&O5WXPE{ji$M+j~!-6YUB_d&q zL8{FH^`+A!u{)BtlhejnzTeChvW8=%Wn#m26+dL0-e1h5DKW!v;$#*>xnw?#NS`wA zGs;2fN2txLK?sg9naNe?bDGViqE2e;6~mp9T(rZyE`Ef@H_xRu8_$ZmP8A(={^Ss+$q#2 z4H#3!nHjFdDy`49joegrb~58EB%_vVU9L(@CN0#)5I$lVr%mg=>VT`>unuI>X(L~y zmrV@8tve0NM$uk7IGmq0tsE-H%qO$g<-+XUl4HKA5_irbJZ@x@XQXFW$IBiZwy+XX z&b0%qAeo=74tQA=x|5m8WHV09s8?#`(wIDfET!=hQ6_lOac>?nB5xUGhJj3SCU4qU zCw5%IMmCmPU(qp#vPJuZ1@{1PW{Pz%E)FV3X>?|JmG8ORg|#;$ll5y zH>SPqhz6#r1?H}Wa=Fnob~FzA!YUn;O^6Pe&M+^qoRLS#4VE`2>YJIdj2V<+m4vlW zMNC)RbA&gZIAWfIm4*8cGIuJ2ZIRqfqq<5e1J|sb%19sY%hznM*u9-ZV#?iZ5&k@D zXD)BcKzYuGin%Gn8k?T9GN2^z_^3g5?(Rt>9DGMEYp#@DoL#Sw%LWI}I7XfqTxFzY zL!}{EaWpn{FLo4~95-w{+<4wz51I0MqnVUt+UB$qmlW|Pnav>8ge}5yXw*Dq9O6&5 zG6D0nm!-;T#g0?W*x;E3Rb89Wp(F=#_Uav;Xks_M_gnNXZWdjdThN^XbP zFvK!qjz^+6bR~gt-=rBx9YSk?asr1XHj%DKInX3IB1E+Sx7tR7eQr9 zlc46%H$ibQwgpt`P16n>K>I;sk4)jHPHqU=IT-B2EGFg98Qe7gbRQJvq3kqTc8i6R zXj7m?!9EQ*hsc!Bvg14frccqm;9!-*;_@!x>lVHYv_vsxfulUBxQuUxlTjMQ-))de z1I~JXQG7c;fYBWOc0k^Slhg87BI>T4R&`P%@M8u(rRXqhnMhCuIyp%+|KRmpbf>Zj zZm??{@f;PrdwV+%`0AR1 z{?^E_ij3BEP-HaN8kv6*h~63*R2?vgR|KX#4I0~M!G{H{x%sB{5D?Hm3pyOC17oBY zmLv0c!G)7`LDd>w2Mu~_xQGUyoVWvVmmCy2K5XcbXoDDQjl3VjnqC*|!fVb_h=e>9 zby0E${XrFu$sEr4L+G;6qmf{+E)s2l`je2yG}w8VhTqW36ASTNTn(VG71k*1;AAPx zKx2=%?>QFg(lqvBX@_bKY3`7#mctMkZ4Bx#8!mYUVIgu9{)9_>*4ux>=(0xP{NB{q&Kp?u}f(k)b0kotxNgXPmkbs!$+E;zQ|Ew zi5v|d(W4?*r@Z%#4t^b()Y|T{l7&NNzVhh9&TXEy75L74#U!kzk zJ3H(;wK?7m14WyJ68cAKIm8+b=1PA4L3EU*+q-LvMeN)aYT3&;{Jz!jAYY z{4iC%m=)iI=@#4t{76vfD37=;f4{B0#wP|Q2LGh(OMm!BTZVqK`Kv!~Jez-+bqwq{ zHf|J5``DymJIDIXMP6%o#6DI#t?C=pF>~r(D3Guo%NtG#DNGg$NZg_Vi{>U?NKetk zfxgYXTgh`#FT02^B)gn0Elm$ zcnn(5lfgj+Z=`tgPvc3-*E*4!#3-K!Y>e{*(>+ZI13l|Mj^8;HeB$E?pB`ow(X>-| zx^RYY|9WtCKnx&*-?ngI;iF1HY|LUtNnqG=aP^BtJ_(FK_mud-hnK{HLk`Qc&l%X} z15*;5Jd?#v3HTjHY?f!sCqY}zDH%BeROj|y*zUqUuPX7m6Sgf_uiEU!iKnY_v#)XR zb4K`(Vxi^3WHmzn_ddpPddlLD@QshZ(~ZFHj9BF;7Z7{S@(fOZI6BH+@*Cpgm`CRd zU?0ozWz5ayd+DoNNA3wQjT+!HX*EjkO&xXN5T(3RXW8b}8Lj4%Xq)7|5Batc)11`k jEFyS)8Low2U%xUDK(hFE=nHOp=zFW`-;6`M&e`&g*;JbMGDagU^wVi2V3nxIpwYTE2D&{9rHxcJtzA zn(3+fGfSRU{b!bp9?9C#Nz0tDQu$~)RVbKFbj*la#X>Y&i1rMPMDu3Gh&MHbmU*i8 z?jq_}KAQT*+$YP{UZZQH4Jtvj2$VXP+Pf8P6u%w#5w%EPbNyz5&Va8O{6Oc+M~ClY zQU0&C9?B$K|9Ktxtu7`Mx=!L+k01d!lh#iGv}qoA z3LdD~)!?5(L@KrrJrlY1#22qwyw1L+Oj}8`Frm}Eun)aq8|y9B_3^`-6HoIu>6h);{b-&H^V8Yf|z8BN*=ZqB#WV+m_*-YDql0=4n%;Z|%FHXZiM z>OG5L>_Tfm3kT-kF7CLLpd|k$GbI$6U~_)q29wjESVR_=WJp5GD~$TDj%GO+vJym*p^@Qd=;Mai|x$GM#9z zpXr!P`mAq`uG8xR|4Ms5Fo)LSZl}}x+Esp?e&~A=a8lqJfqMl0xxl~InEMI+UVn)0 z2|T8U=uQ7)(DqM(SD@h+>Rxcy62lofu5`LC!2KS|Lgqey5dB;H+#k@GJ}mHcjs5wG zz-t6r#FPU9+kEc_boyR^DPIt%VN{6Dz-}{r-+x1(neNfvg!PbUTjyuZZ-_r9#fMu2 zJ|JGTh(8;J@*X5Z^tR$CZqpt^4v3y%fDH0q3+ch#^n-%E zqPtj#PU1LS=kXe;9@su$cR;d9BvXNN`UYTg$Oj(J=N*{2R>A&Eum)9rbOBQ;*j7F_F1G*>WJOA}pRgEi9@S5kD&p@GUc zp0PuMxqgP}2@hjGTj{+Dc3#b+o~3N>b@c+))dD)c%*7JgLSPSg81t^C)61*8tEr_$ z5sE0}>}fy^`K|4@wD{=ZSt#zP4~DGgR;i8JsraaP_DO1)~;mQYx+o(1X~*l7#Y zq;?(Fd8@XXQX+Gb{zY|AR{B$b8)=F*`?t~x^%K2|I@FB5n>Irp{y@(VYH7ALm;PH> z)K1@49}}7TAoE4YY*t;casZO2C=005-LzeQnC^D_YER%Ruyu}3)0AR5d-8Q!Mp^AU zv|4t{YFYiOWzVdZy|Ij*)8B&3ZvTIgQdupalwyifyZtSyQ)D`2RHy8&PT5_ZqG7A} z*+V}H^r?ROi8=sz-gTW~Ym~mDa-ct?TX>FH8n|DL(rNu;=yMJa$=GjU>}hpQ#wx|~ z=OC}cI?F4?k`bKff2ksvJ%-B#t`N9JV28jB0yhiXCUBp?6kr_phgK~P-`W8e(v5&i zsRwWs{Sn}L`T^jL^kcwodKK^%p^pGARcXrjx&=nHgY=fR1MoWEj5ZDW_XYBbBbGNxWlt5-+cU}J`okU6 zTP&o}^zAAX^M;ig%Nf8%Q}$g<>^5^bBkg3(0x%iIoB_i*VrF(0v$>36dD30SQdvi^ zzW%K3@Zf^8ejRN!j;D=D)?jbnkW7M*<#zq$&UDh&0W(v~8QZBlm$L2t?3k6Zrjp%7 z+cERq!~HZeWjjVb-Zxb4^`x8>PkF z*|cZpN+MPC^>s>Wvc6_%{Ff6!o$&4gzF-W!3L0y>)NfP%J?9{k1ROmMIlR3jN zG7?yyUBw5DW4nvl3?(NRj@+FCE1xQ4kn*GET?Ta{*=Eilq{kS|<_)^d!rb&{3kLPc zByzyUKq`HNNh5}H5zfw4nEO+?qA@x(39-9#)s*c_*)U@nkQDDk0oyIOtHs5)ZtqA+ zteyuD$WLZ-h9!By*i4s$dUFg*l6s7>;=}|mzbdWEwvGH)ZfZ2^TujFDt#!FNVl-u8 z74+hrl5xzm?y53e^@eqGHj^<5ReIS(FWlO1ST@$_<;=dqxM}6FM$AGgcSR{g?=CKu zt15A0mcheDE_Gabc9mcDV6TP6pLQ;1u!2-!s>&EH797l~(4Fj9HkWm3xZa{FZ;{a> zn57JA`ZB>A9rxl8juic5WB+s&9kVxAw2#QN@@D_d_fb5P%XwC)J5@*@yQ+oE{dF|Beod$OsCf@$MW zv&*FNw7bjEutp5)Xf|!w-p0c+z~Kv&V;(H##|&#|eALQ7GnsVo_O`6K zvclr*x=p>ZS7>PLPMifaI&9du6LBbB56Sm>1KG4?+UB?um#p(amdj%H2%Fn-Xuv#b z4D!2A@yR^qCBCxHa8^_&Vb}4C7HaJz^`>cS$Pm>n7xnuU6>gA$O5LcTypG3G9CG88A8`I8ZJEf5)Q!!Q$yQ)rK% zXG6)27}kejSuw|)C<>SqFz%Z)fl-5KEl`f2@L&^}$|whVC%|KQHW?+hmxhCuXq>`m z2mK4H1^08iE41Y(9X$VAEpqLL&x!>nMb>zumEMp z(6U=RIE6M1S`^~rfYZoK2`xL$0Wf`x?t}!Z>=T!_iCl-sWuYYso&||=P;ud}gOgDj zz~5So$^cGze^I=Lz8Ty+{Q8&)-_e)ur~pVG7!woFV=7CL#7Xy)Tr za?uTC4cuVYIOBPmM7srHy6!V?ntnG|qY~gW=A^{M=)pzmGrv6iJo&YksmMUxB8m(I+afbx1ERM@cBw@$h;o~0kAlWFTJeysH8)?=9s&aTw?KzO zbr6hf2ms7{mo3-T1yx&k88ql^;UXG*jo}W4yX2tI@sdD~L>t6dTjW--HN7sFz#~Ho zhJ-v7by0Ge{-6rSB!-jz5V~yiKqMHfi$q(Y{%(v%80=EWRpB~18UwGyu`^DlQl<<%g*$~;>m{8gxYIl=9FQI(wr$=zR;eAa} zU*v#@L=J@a=}{SoM+~GE*CKwwGpfzI%}PxUnuW>@4d=0W%vO-&rw*b4MeXsyU89w% zXtj6i--b7W8{-{N(mc1q?8(}bxzyAkDyX?UDq7)0MLMo%#cbtyx%j#0P{v0lMM0E3 z2G@&%bl@c<{_sjrQ9ux)n-p!S)fvCi9d^~MPcXDi(Wc*VJU%-3vG2;)#HZhSp47lz z&fJ&r>%>XfiH|?!i$(F?m9EF{40va1cr3&3NNca*k)Dy2FB-pi`SvGve5rBg!~b^h zdvCLjo-K!ljY-o!G-}w+p>A`QrxNDahib#BJ_#K%$L@pzN$a74;dE~7m{oGtNr_Kp z#%Sc`u5}wWlIPp$pLxC^{+_`%+uv#Xg@$;5U^-_{=(Uv7_!62kh_5=q$(Ij&I)q zAZH|6zWn^;I4Q?)w(rL43I_f9Kk7hdDI`}2hxEZ``(T*KJl^G)8Z07Zbl=|7AQ5ehzWNyQb< z#4MV&icA7^kNek&dIvec7<}78k;UcJq}Z53M0jTbKlthvi(I|*L-&~Y!39_HqQZ>F zv(H)B<{~czNseT;%7*BK*eplO)vGPFRaVNDYHS~d?F8;ks*<1mux-J5)n*5(v_$2{ zzQ!TXGs18sIv*nxzkC9VJls;kymuHjy*e=A|=Qoll}oGWT7W qZyPzy%Nm_R2CqDZ*TP5F?@b0US^ORO=9-$|_r~{+9Q|Ks;C}#JyVw{2 diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs index 296a70dfd8..234b60c9e6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs @@ -506,122 +506,134 @@ public async void RetryExecuteAsyncCancel(string cnnString, SqlRetryLogicBasePro [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper))] public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider provider) { - var cnnStr = new SqlConnectionStringBuilder(cnnString) { MultipleActiveResultSets = true, ConnectTimeout = 0 }.ConnectionString; int numberOfTries = provider.RetryLogic.NumberOfTries; string query = "SELECT bad command"; int retriesCount = 0; - int concurrentExecution = 5; + int concurrentExecution = 3; provider.Retrying += (s, e) => Interlocked.Increment(ref retriesCount); - using (SqlConnection cnn = new SqlConnection(cnnStr)) + Parallel.For(0, concurrentExecution, + i => { - cnn.Open(); - - Parallel.For(0, concurrentExecution, - i => + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) { - using (SqlCommand cmd = cnn.CreateCommand()) - { - cmd.RetryLogicProvider = provider; - cmd.CommandText = query; - Assert.Throws(() => cmd.ExecuteScalar()); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query; + Assert.Throws(() => cmd.ExecuteScalar()); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => + { + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) { - using (SqlCommand cmd = cnn.CreateCommand()) - { - cmd.RetryLogicProvider = provider; - cmd.CommandText = query; - Assert.Throws(() => cmd.ExecuteNonQuery()); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query; + Assert.Throws(() => cmd.ExecuteNonQuery()); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => + { + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) { - using (SqlCommand cmd = cnn.CreateCommand()) - { - cmd.RetryLogicProvider = provider; - cmd.CommandText = query; - Assert.Throws(() => cmd.ExecuteReader()); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query; + Assert.Throws(() => cmd.ExecuteReader()); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => + { + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) { - using (SqlCommand cmd = cnn.CreateCommand()) - { - cmd.RetryLogicProvider = provider; - cmd.CommandText = query + " FOR XML AUTO"; - Assert.Throws(() => cmd.ExecuteXmlReader()); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query + " FOR XML AUTO"; + Assert.Throws(() => cmd.ExecuteXmlReader()); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => + { + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) { - using (SqlCommand cmd = cnn.CreateCommand()) - { - cmd.RetryLogicProvider = provider; - cmd.CommandText = query; - Assert.ThrowsAsync(() => cmd.ExecuteScalarAsync()).Wait(); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query; + Assert.ThrowsAsync(() => cmd.ExecuteScalarAsync()).Wait(); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => + { + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) { - using (SqlCommand cmd = cnn.CreateCommand()) - { - cmd.RetryLogicProvider = provider; - cmd.CommandText = query; - Assert.ThrowsAsync(() => cmd.ExecuteNonQueryAsync()).Wait(); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query; + Assert.ThrowsAsync(() => cmd.ExecuteNonQueryAsync()).Wait(); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => + { + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) { - using (SqlCommand cmd = cnn.CreateCommand()) - { - cmd.RetryLogicProvider = provider; - cmd.CommandText = query; - Assert.ThrowsAsync(() => cmd.ExecuteReaderAsync()).Wait(); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query; + Assert.ThrowsAsync(() => cmd.ExecuteReaderAsync()).Wait(); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + + // TODO: there is a known issue by ExecuteXmlReaderAsync that should be solved first- issue #44 + /* + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => + { + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) { - using (SqlCommand cmd = cnn.CreateCommand()) - { - cmd.RetryLogicProvider = provider; - cmd.CommandText = query + " FOR XML AUTO"; - Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()).Wait(); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - } + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query + " FOR XML AUTO"; + Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()).Wait(); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + */ } - #endregion #region private members diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs index 781e2f7472..ca1bd1ce87 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs @@ -72,13 +72,13 @@ public void LoadValidInternalTypesWithoutEnablingSwitch(string method1, string m #region External Functions [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData("ClassLibrary.StaticCustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry_static")] - [InlineData("ClassLibrary.StructCustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry_static")] - [InlineData("ClassLibrary.StructCustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry")] - [InlineData("ClassLibrary.CustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry_static")] - [InlineData("ClassLibrary.CustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry")] - [InlineData("ClassLibrary.CustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "GetDefaultRetry")] - [InlineData("ClassLibrary.CustomConfigurableRetryLogicEx, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry")] + [InlineData("Microsoft.Data.SqlClient.Tests.StaticCustomConfigurableRetryLogic, ExternalConfigurableRetryLogic", "GetDefaultRetry_static")] + [InlineData("Microsoft.Data.SqlClient.Tests.StructCustomConfigurableRetryLogic, ExternalConfigurableRetryLogic", "GetDefaultRetry_static")] + [InlineData("Microsoft.Data.SqlClient.Tests.StructCustomConfigurableRetryLogic, ExternalConfigurableRetryLogic", "GetDefaultRetry")] + [InlineData("Microsoft.Data.SqlClient.Tests.CustomConfigurableRetryLogic, ExternalConfigurableRetryLogic", "GetDefaultRetry_static")] + [InlineData("Microsoft.Data.SqlClient.Tests.CustomConfigurableRetryLogic, ExternalConfigurableRetryLogic", "GetDefaultRetry")] + [InlineData("Microsoft.Data.SqlClient.Tests.CustomConfigurableRetryLogic, ExternalConfigurableRetryLogic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "GetDefaultRetry")] + [InlineData("Microsoft.Data.SqlClient.Tests.CustomConfigurableRetryLogicEx, ExternalConfigurableRetryLogic", "GetDefaultRetry")] public void LoadCustomMethod(string typeName, string methodName) { bool switchValue = true; @@ -101,16 +101,16 @@ public void LoadCustomMethod(string typeName, string methodName) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData("ClassLibrary.Invalid, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry_static")] - [InlineData("ClassLibrary.Invalid, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry")] - [InlineData("ClassLibrary.CustomConfigurableRetryLogic, ClassLibrary_Invalid", "GetDefaultRetry_static")] - [InlineData("ClassLibrary.StaticCustomConfigurableRetryLogic, ClassLibrary_Invalid", "GetDefaultRetry_static")] - [InlineData("ClassLibrary.StructCustomConfigurableRetryLogic, ClassLibrary_Invalid", "GetDefaultRetry")] + [InlineData("ClassLibrary.Invalid, ExternalConfigurableRetryLogic", "GetDefaultRetry_static")] + [InlineData("ClassLibrary.Invalid, ExternalConfigurableRetryLogic", "GetDefaultRetry")] + [InlineData("Microsoft.Data.SqlClient.Tests.CustomConfigurableRetryLogic, ClassLibrary_Invalid", "GetDefaultRetry_static")] + [InlineData("Microsoft.Data.SqlClient.Tests.StaticCustomConfigurableRetryLogic, ClassLibrary_Invalid", "GetDefaultRetry_static")] + [InlineData("Microsoft.Data.SqlClient.Tests.StructCustomConfigurableRetryLogic, ClassLibrary_Invalid", "GetDefaultRetry")] // Type and method name are case sensitive. - [InlineData("ClassLibrary.StaticCustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic", "GETDEFAULTRETRY_STATIC")] - [InlineData("ClassLibrary.STRUCTCUSTOMCONFIGURABLERETRYLOGIC, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry")] - [InlineData("CLASSLIBRARY.CustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic", "GetDefaultRetry_static")] - [InlineData("ClassLibrary.CustomConfigurableRetryLogic, ClassLibrary_CustomConfigurableRetryLogic", "getdefaultretry")] + [InlineData("Microsoft.Data.SqlClient.Tests.StaticCustomConfigurableRetryLogic, ExternalConfigurableRetryLogic", "GETDEFAULTRETRY_STATIC")] + [InlineData("Microsoft.Data.SqlClient.Tests.STRUCTCUSTOMCONFIGURABLERETRYLOGIC, ExternalConfigurableRetryLogic", "GetDefaultRetry")] + [InlineData("MICROSOFT.DATA.SQLCLIENT.TESTS.CustomConfigurableRetryLogic, ExternalConfigurableRetryLogic", "GetDefaultRetry_static")] + [InlineData("Microsoft.Data.SqlClient.Tests.CustomConfigurableRetryLogic, ExternalConfigurableRetryLogic", "getdefaultretry")] public void LoadInvalidCustomRetryLogicType(string typeName, string methodName) { bool switchValue = true; From 37f7fbdf66a651a38178cded703b0fe80bc3aaaf Mon Sep 17 00:00:00 2001 From: Wraith Date: Wed, 28 Apr 2021 19:26:50 +0100 Subject: [PATCH 19/87] Sync Crypto api usage (#1022) --- .../src/Microsoft.Data.SqlClient.csproj | 28 +- ...aysEncryptedKeyConverter.CrossPlatform.cs} | 127 ++-- ...EnclaveAttestationParameters.NetCoreApp.cs | 27 - .../SqlEnclaveAttestationParameters.cs | 44 -- .../netfx/src/Microsoft.Data.SqlClient.csproj | 23 +- .../AlwaysEncryptedKeyConverter.Cng.cs | 71 +++ .../AzureAttestationBasedEnclaveProvider.cs | 545 ------------------ .../SqlClient/EnclaveDelegate.CngCryto.cs | 231 -------- .../SqlColumnEncryptionEnclaveProvider.cs | 2 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 3 +- .../Data/SqlClient/SqlCommandBuilder.cs | 1 + .../Microsoft/Data/SqlClient/SqlConnection.cs | 5 +- .../Data/SqlClient/SqlDataAdapter.cs | 3 +- .../SqlEnclaveAttestationParameters.cs | 69 --- .../VirtualSecureModeEnclaveProviderBase.cs | 367 ------------ .../AzureAttestationBasedEnclaveProvider.cs | 14 +- .../Data/SqlClient/EnclaveDelegate.Crypto.cs} | 2 +- .../EnclaveDelegate.NotSupported.cs} | 0 .../SqlEnclaveAttestationParameters.Crypto.cs | 52 ++ ...claveAttestationParameters.NotSupported.cs | 19 + .../VirtualSecureModeEnclaveProviderBase.cs | 23 +- 21 files changed, 275 insertions(+), 1381 deletions(-) rename src/Microsoft.Data.SqlClient/{src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs => netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.CrossPlatform.cs} (72%) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.Cng.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs (97%) rename src/Microsoft.Data.SqlClient/{netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs => src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs} (98%) rename src/Microsoft.Data.SqlClient/{netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs => src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs} (100%) create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.Crypto.cs create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NotSupported.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs (93%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 4546d7d1ab..1b74587f24 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -318,7 +318,12 @@ - + + Microsoft\Data\SqlClient\EnclaveDelegate.NotSupported.cs + + + Microsoft\Data\SqlClient\SqlEnclaveAttestationParameters.NotSupported.cs + @@ -335,9 +340,7 @@ Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs - - Microsoft\Data\SqlClient\AlwaysEncryptedKeyConverter.cs - + Microsoft\Data\SqlClient\EnclaveProviderBase.cs @@ -345,13 +348,21 @@ Microsoft\Data\SqlClient\EnclaveSessionCache.cs - - - + + Microsoft\Data\SqlClient\SqlEnclaveAttestationParameters.Crypto.cs + + + Microsoft\Data\SqlClient\EnclaveDelegate.Crypto.cs + + + Microsoft\Data\SqlClient\AzureAttestationBasedEnclaveProvider.cs + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs - + + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProviderBase.cs + @@ -523,7 +534,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.CrossPlatform.cs similarity index 72% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.CrossPlatform.cs index 6ccbd67fb6..8b3def875c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.CrossPlatform.cs @@ -5,12 +5,56 @@ using System; using System.Diagnostics; using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; namespace Microsoft.Data.SqlClient { - // Contains methods to convert cryptography keys between different formats. - internal sealed class KeyConverter - { + internal sealed partial class KeyConverter + { + // Magic numbers identifying blob types + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/cba27df5-4880-4f95-a879-783f8657e53b + private readonly struct KeyBlobMagicNumber + { + internal static readonly byte[] ECDHPublicP384 = new byte[] { 0x45, 0x43, 0x4b, 0x33 }; + } + + // The ECC public key blob is structured as follows: + // BCRYPT_ECCKEY_BLOB header + // byte[KeySize] X + // byte[KeySize] Y + private readonly struct ECCPublicKeyBlob + { + // Size of an ECC public key blob + internal const int Size = 104; + // Size of the BCRYPT_ECCKEY_BLOB header + internal const int HeaderSize = 8; + // Size of each coordinate + internal const int KeySize = (Size - HeaderSize) / 2; + } + + // Serializes an ECDiffieHellmanPublicKey to an ECC public key blob + // "ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export + // format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific" + // from https://github.com/dotnet/runtime/issues/27276 + // => ECDiffieHellmanPublicKey.ToByteArray() is not supported in Unix + internal static byte[] GetECDiffieHellmanPublicKeyBlob(ECDiffieHellman ecDiffieHellman) + { + byte[] keyBlob = new byte[ECCPublicKeyBlob.Size]; + + // Set magic number + Buffer.BlockCopy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4); + // Set key size + keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize; + + ECPoint ecPoint = ecDiffieHellman.PublicKey.ExportParameters().Q; + Debug.Assert(ecPoint.X.Length == ECCPublicKeyBlob.KeySize && ecPoint.Y.Length == ECCPublicKeyBlob.KeySize, + $"ECDH public key was not the expected length. Actual (X): {ecPoint.X.Length}. Actual (Y): {ecPoint.Y.Length} Expected: {ECCPublicKeyBlob.Size}"); + // Copy x and y coordinates to key blob + Buffer.BlockCopy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize); + Buffer.BlockCopy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize); + return keyBlob; + } + // The RSA public key blob is structured as follows: // BCRYPT_RSAKEY_BLOB header // byte[ExponentSize] publicExponent @@ -29,59 +73,32 @@ internal sealed class KeyConverter internal const int ModulusOffset = HeaderSize; } - // Extracts the public key's modulus and exponent from an RSA public key blob - // and returns an RSAParameters object - internal static RSAParameters RSAPublicKeyBlobToParams(byte[] keyBlob) + internal static RSA CreateRSAFromPublicKeyBlob(byte[] keyBlob) { - Debug.Assert(keyBlob.Length == RSAPublicKeyBlob.Size, - $"RSA public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {RSAPublicKeyBlob.Size}"); + Debug.Assert(keyBlob.Length == RSAPublicKeyBlob.Size, $"RSA public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {RSAPublicKeyBlob.Size}"); byte[] exponent = new byte[RSAPublicKeyBlob.ExponentSize]; byte[] modulus = new byte[RSAPublicKeyBlob.ModulusSize]; Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ExponentOffset, exponent, 0, RSAPublicKeyBlob.ExponentSize); Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ModulusOffset, modulus, 0, RSAPublicKeyBlob.ModulusSize); - - return new RSAParameters() + var rsaParameters = new RSAParameters() { Exponent = exponent, Modulus = modulus }; + return RSA.Create(rsaParameters); } - // The ECC public key blob is structured as follows: - // BCRYPT_ECCKEY_BLOB header - // byte[KeySize] X - // byte[KeySize] Y - private readonly struct ECCPublicKeyBlob - { - // Size of an ECC public key blob - internal const int Size = 104; - // Size of the BCRYPT_ECCKEY_BLOB header - internal const int HeaderSize = 8; - // Size of each coordinate - internal const int KeySize = (Size - HeaderSize) / 2; - } - - // Magic numbers identifying blob types - // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/cba27df5-4880-4f95-a879-783f8657e53b - private readonly struct KeyBlobMagicNumber - { - internal static readonly byte[] ECDHPublicP384 = new byte[] { 0x45, 0x43, 0x4b, 0x33 }; - } - - // Extracts the public key's X and Y coordinates from an ECC public key blob - // and returns an ECParameters object - internal static ECParameters ECCPublicKeyBlobToParams(byte[] keyBlob) + internal static ECDiffieHellman CreateECDiffieHellmanFromPublicKeyBlob(byte[] keyBlob) { - Debug.Assert(keyBlob.Length == ECCPublicKeyBlob.Size, - $"ECC public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {ECCPublicKeyBlob.Size}"); + Debug.Assert(keyBlob.Length == ECCPublicKeyBlob.Size, $"ECC public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {ECCPublicKeyBlob.Size}"); byte[] x = new byte[ECCPublicKeyBlob.KeySize]; byte[] y = new byte[ECCPublicKeyBlob.KeySize]; Buffer.BlockCopy(keyBlob, ECCPublicKeyBlob.HeaderSize, x, 0, ECCPublicKeyBlob.KeySize); Buffer.BlockCopy(keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, y, 0, ECCPublicKeyBlob.KeySize); - return new ECParameters + var parameters = new ECParameters { Curve = ECCurve.NamedCurves.nistP384, Q = new ECPoint @@ -90,29 +107,29 @@ internal static ECParameters ECCPublicKeyBlobToParams(byte[] keyBlob) Y = y }, }; + + return ECDiffieHellman.Create(parameters); } - // Serializes an ECDiffieHellmanPublicKey to an ECC public key blob - // "ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export - // format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific" - // from https://github.com/dotnet/runtime/issues/27276 - // => ECDiffieHellmanPublicKey.ToByteArray() is not supported in Unix - internal static byte[] ECDHPublicKeyToECCKeyBlob(ECDiffieHellmanPublicKey publicKey) + internal static ECDiffieHellman CreateECDiffieHellman(int keySize) { - byte[] keyBlob = new byte[ECCPublicKeyBlob.Size]; + // platform agnostic creates a key of the correct size but does not + // set the key derivation type or algorithm, these must be set by calling + // DeriveKeyFromHash later in DeriveKey + ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); + clientDHKey.KeySize = keySize; + return clientDHKey; + } - // Set magic number - Buffer.BlockCopy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4); - // Set key size - keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize; + internal static byte[] DeriveKey(ECDiffieHellman ecd, ECDiffieHellmanPublicKey publicKey) + { + // see notes in CreateECDDiffieHellman + return ecd.DeriveKeyFromHash(publicKey, HashAlgorithmName.SHA256); + } - ECPoint ecPoint = publicKey.ExportParameters().Q; - Debug.Assert(ecPoint.X.Length == ECCPublicKeyBlob.KeySize && ecPoint.Y.Length == ECCPublicKeyBlob.KeySize, - $"ECDH public key was not the expected length. Actual (X): {ecPoint.X.Length}. Actual (Y): {ecPoint.Y.Length} Expected: {ECCPublicKeyBlob.Size}"); - // Copy x and y coordinates to key blob - Buffer.BlockCopy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize); - Buffer.BlockCopy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize); - return keyBlob; + internal static RSA GetRSAFromCertificate(X509Certificate2 certificate) + { + return certificate.GetRSAPublicKey(); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs deleted file mode 100644 index 739187a7e9..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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.Security.Cryptography; - -namespace Microsoft.Data.SqlClient -{ - /// - internal partial class SqlEnclaveAttestationParameters - { - private static readonly string _clientDiffieHellmanKeyName = "ClientDiffieHellmanKey"; - private static readonly string _inputName = "input"; - private static readonly string _className = "EnclaveAttestationParameters"; - - /// - internal ECDiffieHellman ClientDiffieHellmanKey { get; } - - /// - internal SqlEnclaveAttestationParameters(int protocol, byte[] input, ECDiffieHellman clientDiffieHellmanKey) - { - _input = input ?? throw SQL.NullArgumentInConstructorInternal(_inputName, _className); - Protocol = protocol; - ClientDiffieHellmanKey = clientDiffieHellmanKey ?? throw SQL.NullArgumentInConstructorInternal(_clientDiffieHellmanKeyName, _className); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs deleted file mode 100644 index 25f2737c70..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs +++ /dev/null @@ -1,44 +0,0 @@ -// 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 -{ - /// - internal partial class SqlEnclaveAttestationParameters - { - private readonly byte[] _input = null; - - /// - internal int Protocol { get; } - - /// - internal byte[] GetInput() - { - return Clone(_input); - } - - /// - /// Deep copy the array into a new array - /// - /// - /// - private byte[] Clone(byte[] arrayToClone) - { - - if (null == arrayToClone) - { - return null; - } - - byte[] returnValue = new byte[arrayToClone.Length]; - - for (int i = 0; i < arrayToClone.Length; i++) - { - returnValue[i] = arrayToClone[i]; - } - - return returnValue; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index d80ca43a2d..1ad37256a2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -151,9 +151,16 @@ Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs + + + Microsoft\Data\SqlClient\AzureAttestationBasedEnclaveProvider.cs + Microsoft\Data\SqlClient\EnclaveDelegate.cs + + Microsoft\Data\SqlClient\EnclaveDelegate.Crypto.cs + Microsoft\Data\SqlClient\EnclavePackage.cs @@ -283,6 +290,12 @@ Microsoft\Data\SqlClient\SqlConnectionPoolProviderInfo.cs + + Microsoft\Data\SqlClient\SqlEnclaveAttestationParameters.Crypto.cs + + + Microsoft\Data\SqlClient\SqlEnclaveSession.cs + Microsoft\Data\SqlClient\SqlInfoMessageEventHandler.cs @@ -319,6 +332,9 @@ Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs + + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProviderBase.cs + Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs @@ -403,15 +419,12 @@ - - - @@ -449,10 +462,6 @@ - - - Microsoft\Data\SqlClient\SqlEnclaveSession.cs - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.Cng.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.Cng.cs new file mode 100644 index 0000000000..f0d9943ca8 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.Cng.cs @@ -0,0 +1,71 @@ +// 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.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace Microsoft.Data.SqlClient +{ + internal sealed partial class KeyConverter + { + internal static RSA CreateRSAFromPublicKeyBlob(byte[] keyBlob) + { + CngKey key = CngKey.Import(keyBlob, CngKeyBlobFormat.GenericPublicBlob); + return new RSACng(key); + } + + internal static ECDiffieHellman CreateECDiffieHellmanFromPublicKeyBlob(byte[] keyBlob) + { + CngKey key = CngKey.Import(keyBlob, CngKeyBlobFormat.GenericPublicBlob); + return new ECDiffieHellmanCng(key); + } + + internal static ECDiffieHellman CreateECDiffieHellman(int keySize) + { + // Cng sets the key size and hash algorithm at creation time and these + // parameters are then used later when DeriveKeyMaterial is called + ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(keySize); + clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; + clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; + return clientDHKey; + } + + public static byte[] GetECDiffieHellmanPublicKeyBlob(ECDiffieHellman ecDiffieHellman) + { + if (ecDiffieHellman is ECDiffieHellmanCng cng) + { + return cng.Key.Export(CngKeyBlobFormat.EccPublicBlob); + } + else + { + throw new InvalidOperationException(); + } + } + + internal static byte[] DeriveKey(ECDiffieHellman ecDiffieHellman, ECDiffieHellmanPublicKey publicKey) + { + if (ecDiffieHellman is ECDiffieHellmanCng cng) + { + return cng.DeriveKeyMaterial(publicKey); + } + else + { + throw new InvalidOperationException(); + } + } + + internal static RSA GetRSAFromCertificate(X509Certificate2 certificate) + { + RSAParameters parameters; + using (RSA rsaCsp = certificate.GetRSAPublicKey()) + { + parameters = rsaCsp.ExportParameters(includePrivateParameters: false); + } + RSACng rsaCng = new RSACng(); + rsaCng.ImportParameters(parameters); + return rsaCng; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs deleted file mode 100644 index 84a92c7ccc..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ /dev/null @@ -1,545 +0,0 @@ -// 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.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.Linq; -using System.Runtime.Caching; -using System.Security.Claims; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using Microsoft.IdentityModel.JsonWebTokens; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.Protocols; -using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using Microsoft.IdentityModel.Tokens; - -// Azure Attestation Protocol Flow -// To start the attestation process, Sql Client sends the Protocol Id (i.e. 1), Nonce, Attestation Url and ECDH Public Key -// Sql Server uses attestation Url to attest the enclave and send the JWT to Sql client. -// Along with JWT, Sql server also sends enclave RSA public key, enclave Type, Enclave ECDH Public key. - -// To verify the chain of trust here is how it works -// JWT is signed by well-known signing keys which Sql client can download over https (via OpenIdConnect protocol). -// JWT contains the Enclave public key to safeguard against spoofing enclave RSA public key. -// Enclave ECDH public key signed by enclave RSA key - -// JWT validation -// To get the signing key for the JWT, we use OpenIdConnect API's. It download the signing keys from the well-known endpoint. -// We validate that JWT is signed, valid (i.e. not expired) and check the Issuer. - -// Claim validation: -// Validate the RSA public key send by Sql server matches the value specified in JWT. - -// Enclave Specific checks -// VSM -// Validate the nonce send by Sql client during start of attestation is same as that of specified in the JWT - -// SGX -// JWT for SGX enclave does not contain nonce claim. To workaround this limitation Sql Server sends the RSA public key XOR with the Nonce. -// In Sql server tempered with the nonce value then both Sql Server and client will not able to compute the same shared secret. - -namespace Microsoft.Data.SqlClient -{ - // Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation - internal class AzureAttestationEnclaveProvider : EnclaveProviderBase - { - #region Constants - private const int DiffieHellmanKeySize = 384; - private const int AzureBasedAttestationProtocolId = 1; - private const int SigningKeyRetryInSec = 3; - #endregion - - #region Members - // this is meta data endpoint for AAS provided by Windows team - // i.e. https:///.well-known/openid-configuration - // such as https://sql.azure.attest.com/.well-known/openid-configuration - private const string AttestationUrlSuffix = @"/.well-known/openid-configuration"; - - private static readonly MemoryCache OpenIdConnectConfigurationCache = new MemoryCache("OpenIdConnectConfigurationCache"); - #endregion - - #region Internal methods - // When overridden in a derived class, looks up an existing enclave session information in the enclave session cache. - // If the enclave provider doesn't implement enclave session caching, this method is expected to return null in the sqlEnclaveSession parameter. - internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) - { - GetEnclaveSessionHelper(enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength); - } - - // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) - { - ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); - clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; - clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; - byte[] attestationParam = PrepareAttestationParameters(attestationUrl, customData, customDataLength); - return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, clientDHKey); - } - - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) - { - sqlEnclaveSession = null; - counter = 0; - try - { - ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); - if (sqlEnclaveSession == null) - { - if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl) && customData != null && customDataLength > 0) - { - byte[] nonce = customData; - - IdentityModelEventSource.ShowPII = true; - - // Deserialize the payload - AzureAttestationInfo attestInfo = new AzureAttestationInfo(attestationInfo); - - // Validate the attestation info - VerifyAzureAttestationInfo(enclaveSessionParameters.AttestationUrl, attestInfo.EnclaveType, attestInfo.AttestationToken.AttestationToken, attestInfo.Identity, nonce); - - // Set up shared secret and validate signature - byte[] sharedSecret = GetSharedSecret(attestInfo.Identity, nonce, attestInfo.EnclaveType, attestInfo.EnclaveDHInfo, clientDHKey); - - // add session to cache - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, attestInfo.SessionId, out counter); - } - else - { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); - } - } - } - finally - { - // As per current design, we want to minimize the number of create session calls. To achieve this we block all the GetEnclaveSession calls until the first call to - // GetEnclaveSession -> GetAttestationParameters -> CreateEnclaveSession completes or the event timeout happen. - // Case 1: When the first request successfully creates the session, then all outstanding GetEnclaveSession will use the current session. - // Case 2: When the first request unable to create the enclave session (may be due to some error or the first request doesn't require enclave computation) then in those case we set the event timeout to 0. - UpdateEnclaveSessionLockStatus(sqlEnclaveSession); - } - } - - // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. - internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); - } - #endregion - - #region Internal Class - - // A model class representing the deserialization of the byte payload the client - // receives from SQL Server while setting up a session. - // Protocol format: - // 1. Total Size of the attestation blob as UINT - // 2. Size of Enclave RSA public key as UINT - // 3. Size of Attestation token as UINT - // 4. Enclave Type as UINT - // 5. Enclave RSA public key (raw key, of length #2) - // 6. Attestation token (of length #3) - // 7. Size of Session Id was UINT - // 8. Session id value - // 9. Size of enclave ECDH public key - // 10. Enclave ECDH public key (of length #9) - internal class AzureAttestationInfo - { - public uint TotalSize { get; set; } - - // The enclave's RSA Public Key. - // Needed to establish trust of the enclave. - // Used to verify the enclave's DiffieHellman info. - public EnclavePublicKey Identity { get; set; } - - // The enclave report from the SQL Server host's enclave. - public AzureAttestationToken AttestationToken { get; set; } - - // The id of the current session. - // Needed to set up a secure session between the client and enclave. - public long SessionId { get; set; } - - public EnclaveType EnclaveType { get; set; } - - // The DiffieHellman public key and signature of SQL Server host's enclave. - // Needed to set up a secure session between the client and enclave. - public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } - - public AzureAttestationInfo(byte[] attestationInfo) - { - try - { - int offset = 0; - - // Total size of the attestation info buffer - TotalSize = BitConverter.ToUInt32(attestationInfo, offset); - offset += sizeof(uint); - - // Size of the Enclave public key - int identitySize = BitConverter.ToInt32(attestationInfo, offset); - offset += sizeof(uint); - - // Size of the Azure attestation token - int attestationTokenSize = BitConverter.ToInt32(attestationInfo, offset); - offset += sizeof(uint); - - // Enclave type - int enclaveType = BitConverter.ToInt32(attestationInfo, offset); - EnclaveType = (EnclaveType)enclaveType; - offset += sizeof(uint); - - // Get the enclave public key - byte[] identityBuffer = attestationInfo.Skip(offset).Take(identitySize).ToArray(); - Identity = new EnclavePublicKey(identityBuffer); - offset += identitySize; - - // Get Azure attestation token - byte[] attestationTokenBuffer = attestationInfo.Skip(offset).Take(attestationTokenSize).ToArray(); - AttestationToken = new AzureAttestationToken(attestationTokenBuffer); - offset += attestationTokenSize; - - uint secureSessionInfoResponseSize = BitConverter.ToUInt32(attestationInfo, offset); - offset += sizeof(uint); - - SessionId = BitConverter.ToInt64(attestationInfo, offset); - offset += sizeof(long); - - int secureSessionBufferSize = Convert.ToInt32(secureSessionInfoResponseSize) - sizeof(uint); - byte[] secureSessionBuffer = attestationInfo.Skip(offset).Take(secureSessionBufferSize).ToArray(); - EnclaveDHInfo = new EnclaveDiffieHellmanInfo(secureSessionBuffer); - offset += Convert.ToInt32(EnclaveDHInfo.Size); - } - catch (Exception exception) - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.FailToParseAttestationInfo, exception.Message)); - } - } - } - - // A managed model representing the output of EnclaveGetAttestationReport - // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx - internal class AzureAttestationToken - { - public string AttestationToken { get; set; } - - public AzureAttestationToken(byte[] payload) - { - string jwt = System.Text.Encoding.Default.GetString(payload); - AttestationToken = jwt.Trim().Trim('"'); - } - } - #endregion Internal Class - - #region Private helpers - // Prepare the attestation data in following format - // Attestation Url length - // Attestation Url - // Size of nonce - // Nonce value - internal byte[] PrepareAttestationParameters(string attestationUrl, byte[] attestNonce, int attestNonceLength) - { - if (!string.IsNullOrEmpty(attestationUrl) && attestNonce != null && attestNonceLength > 0) - { - // In c# strings are not null terminated, so adding the null termination before serializing it - string attestationUrlLocal = attestationUrl + char.MinValue; - byte[] serializedAttestationUrl = Encoding.Unicode.GetBytes(attestationUrlLocal); - byte[] serializedAttestationUrlLength = BitConverter.GetBytes(serializedAttestationUrl.Length); - - // serializing nonce - byte[] serializedNonce = attestNonce; - byte[] serializedNonceLength = BitConverter.GetBytes(attestNonceLength); - - // Computing the total length of the data - int totalDataSize = serializedAttestationUrl.Length + serializedAttestationUrlLength.Length + serializedNonce.Length + serializedNonceLength.Length; - - int dataCopied = 0; - byte[] attestationParam = new byte[totalDataSize]; - - // copy the attestation url and url length - Buffer.BlockCopy(serializedAttestationUrlLength, 0, attestationParam, dataCopied, serializedAttestationUrlLength.Length); - dataCopied += serializedAttestationUrlLength.Length; - - Buffer.BlockCopy(serializedAttestationUrl, 0, attestationParam, dataCopied, serializedAttestationUrl.Length); - dataCopied += serializedAttestationUrl.Length; - - // copy the nonce and nonce length - Buffer.BlockCopy(serializedNonceLength, 0, attestationParam, dataCopied, serializedNonceLength.Length); - dataCopied += serializedNonceLength.Length; - - Buffer.BlockCopy(serializedNonce, 0, attestationParam, dataCopied, serializedNonce.Length); - dataCopied += serializedNonce.Length; - - return attestationParam; - } - else - { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); - } - } - - // Performs Attestation per the protocol used by Azure Attestation Service - private void VerifyAzureAttestationInfo(string attestationUrl, EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) - { - bool shouldForceUpdateSigningKeys = false; - string attestationInstanceUrl = GetAttestationInstanceUrl(attestationUrl); - - bool shouldRetryValidation; - bool isSignatureValid; - string exceptionMessage = string.Empty; - do - { - shouldRetryValidation = false; - - // Get the OpenId config object for the signing keys - OpenIdConnectConfiguration openIdConfig = GetOpenIdConfigForSigningKeys(attestationInstanceUrl, shouldForceUpdateSigningKeys); - - // Verify the token signature against the signing keys downloaded from meta data end point - bool isKeySigningExpired; - isSignatureValid = VerifyTokenSignature(attestationToken, attestationInstanceUrl, openIdConfig.SigningKeys, out isKeySigningExpired, out exceptionMessage); - - // In cases if we fail to validate the token, since we are using the old signing keys - // let's re-download the signing keys again and re-validate the token signature - if (!isSignatureValid && isKeySigningExpired && !shouldForceUpdateSigningKeys) - { - shouldForceUpdateSigningKeys = true; - shouldRetryValidation = true; - } - } - while (shouldRetryValidation); - - if (!isSignatureValid) - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.AttestationTokenSignatureValidationFailed, exceptionMessage)); - } - - // Validate claims in the token - ValidateAttestationClaims(enclaveType, attestationToken, enclavePublicKey, nonce); - } - - // Returns the innermost exception value - private static string GetInnerMostExceptionMessage(Exception exception) - { - Exception exLocal = exception; - while (exLocal.InnerException != null) - { - exLocal = exLocal.InnerException; - } - - return exLocal.Message; - } - - // For the given attestation url it downloads the token signing keys from the well-known openid configuration end point. - // It also caches that information for 1 day to avoid DDOS attacks. - private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, bool forceUpdate) - { - OpenIdConnectConfiguration openIdConnectConfig = OpenIdConnectConfigurationCache[url] as OpenIdConnectConfiguration; - if (forceUpdate || openIdConnectConfig == null) - { - // Compute the meta data endpoint - string openIdMetadataEndpoint = url + AttestationUrlSuffix; - - try - { - IConfigurationManager configurationManager = new ConfigurationManager(openIdMetadataEndpoint, new OpenIdConnectConfigurationRetriever()); - openIdConnectConfig = configurationManager.GetConfigurationAsync(CancellationToken.None).Result; - } - catch (Exception exception) - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); - } - - OpenIdConnectConfigurationCache.Add(url, openIdConnectConfig, DateTime.UtcNow.AddDays(1)); - } - - return openIdConnectConfig; - } - - // Return the attestation instance url for given attestation url - // such as for https://sql.azure.attest.com/attest/SgxEnclave?api-version=2017-11-01 - // It will return https://sql.azure.attest.com - private string GetAttestationInstanceUrl(string attestationUrl) - { - Uri attestationUri = new Uri(attestationUrl); - return attestationUri.GetLeftPart(UriPartial.Authority); - } - - // Generate the list of valid issuer Url's (in case if tokenIssuerUrl is using default port) - private static ICollection GenerateListOfIssuers(string tokenIssuerUrl) - { - List issuerUrls = new List(); - - Uri tokenIssuerUri = new Uri(tokenIssuerUrl); - int port = tokenIssuerUri.Port; - bool isDefaultPort = tokenIssuerUri.IsDefaultPort; - - string issuerUrl = tokenIssuerUri.GetLeftPart(UriPartial.Authority); - issuerUrls.Add(issuerUrl); - - if (isDefaultPort) - { - issuerUrls.Add(String.Concat(issuerUrl, ":", port.ToString())); - } - - return issuerUrls; - } - - // Verifies the attestation token is signed by correct signing keys. - private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl, ICollection issuerSigningKeys, out bool isKeySigningExpired, out string exceptionMessage) - { - exceptionMessage = string.Empty; - bool isSignatureValid = false; - isKeySigningExpired = false; - - // Configure the TokenValidationParameters - TokenValidationParameters validationParameters = - new TokenValidationParameters - { - RequireExpirationTime = true, - ValidateLifetime = true, - ValidateIssuer = true, - ValidateAudience = false, - RequireSignedTokens = true, - ValidIssuers = GenerateListOfIssuers(tokenIssuerUrl), - IssuerSigningKeys = issuerSigningKeys - }; - - try - { - SecurityToken validatedToken; - JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); - var token = handler.ValidateToken(attestationToken, validationParameters, out validatedToken); - isSignatureValid = true; - } - catch (SecurityTokenExpiredException securityException) - { - throw new AlwaysEncryptedAttestationException(Strings.ExpiredAttestationToken, securityException); - } - catch (SecurityTokenValidationException securityTokenException) - { - isKeySigningExpired = true; - - // Sleep for SigningKeyRetryInSec sec before retrying to download the signing keys again. - Thread.Sleep(SigningKeyRetryInSec * 1000); - exceptionMessage = GetInnerMostExceptionMessage(securityTokenException); - } - catch (Exception exception) - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.InvalidAttestationToken, GetInnerMostExceptionMessage(exception))); - } - - return isSignatureValid; - } - - // Computes the SHA256 hash of the byte array - private byte[] ComputeSHA256(byte[] data) - { - byte[] result = null; - try - { - using (SHA256 sha256 = SHA256.Create()) - { - result = sha256.ComputeHash(data); - } - } - catch (Exception argumentException) - { - throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToSHA256, argumentException); - } - return result; - } - - // Validate the claims in the attestation token - private void ValidateAttestationClaims(EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) - { - // Read the json token - JsonWebToken token = null; - try - { - JsonWebTokenHandler tokenHandler = new JsonWebTokenHandler(); - token = tokenHandler.ReadJsonWebToken(attestationToken); - } - catch (ArgumentException argumentException) - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.FailToParseAttestationToken, argumentException.Message)); - } - - // Get all the claims from the token - Dictionary claims = new Dictionary(); - foreach (Claim claim in token.Claims.ToList()) - { - claims.Add(claim.Type, claim.Value); - } - - // Get Enclave held data claim and validate it with the Base64UrlEncode(enclave public key) - ValidateClaim(claims, "aas-ehd", enclavePublicKey.PublicKey); - - if (enclaveType == EnclaveType.Vbs) - { - // Get rp_data claim and validate it with the Base64UrlEncode(nonce) - ValidateClaim(claims, "rp_data", nonce); - } - } - - // Validate the claim value against the actual data - private void ValidateClaim(Dictionary claims, string claimName, byte[] actualData) - { - // Get required claim data - string claimData; - bool hasClaim = claims.TryGetValue(claimName, out claimData); - if (!hasClaim) - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.MissingClaimInAttestationToken, claimName)); - } - - // Get the Base64Url of the actual data and compare it with claim - string encodedActualData = string.Empty; - try - { - encodedActualData = Base64UrlEncoder.Encode(actualData); - } - catch (Exception) - { - throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToBase64UrlDecoder); - } - - bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.Ordinal); - if (!hasValidClaim) - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.InvalidClaimInAttestationToken, claimName, claimData)); - } - } - - private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, EnclaveType enclaveType, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) - { - byte[] enclaveRsaPublicKey = enclavePublicKey.PublicKey; - - // For SGX enclave we Sql server sends the enclave public key XOR'ed with Nonce. - // In case if Sql server replayed old JWT then shared secret will not match and hence client will not able to determine the updated enclave keys. - if (enclaveType == EnclaveType.Sgx) - { - for (int iterator = 0; iterator < enclaveRsaPublicKey.Length; iterator++) - { - enclaveRsaPublicKey[iterator] = (byte)(enclaveRsaPublicKey[iterator] ^ nonce[iterator % nonce.Length]); - } - } - - // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. - CngKey cngkey = CngKey.Import(enclaveRsaPublicKey, CngKeyBlobFormat.GenericPublicBlob); - using (RSACng rsacng = new RSACng(cngkey)) - { - if (!rsacng.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) - { - throw new ArgumentException(Strings.GetSharedSecretFailed); - } - } - - CngKey key = CngKey.Import(enclaveDHInfo.PublicKey, CngKeyBlobFormat.GenericPublicBlob); - return clientDHKey.DeriveKeyMaterial(key); - } - #endregion - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs deleted file mode 100644 index aceb598436..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs +++ /dev/null @@ -1,231 +0,0 @@ -// 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.Generic; -using System.Security.Cryptography; - -namespace Microsoft.Data.SqlClient -{ - internal sealed partial class EnclaveDelegate - { - private static readonly Dictionary s_enclaveProviders = new Dictionary(); - - /// - /// Create a new enclave session - /// - /// attestation protocol - /// enclave type - /// The set of parameters required for enclave session. - /// attestation info from SQL Server - /// attestation parameters - /// A set of extra data needed for attestating the enclave. - /// The length of the extra data needed for attestating the enclave. - internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, - byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, byte[] customData, int customDataLength) - { - lock (_lock) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - - sqlColumnEncryptionEnclaveProvider.GetEnclaveSession( - enclaveSessionParameters, - generateCustomData: false, - sqlEnclaveSession: out SqlEnclaveSession sqlEnclaveSession, - counter: out _, - customData: out _, - customDataLength: out _ - ); - - if (sqlEnclaveSession != null) - { - return; - } - - sqlColumnEncryptionEnclaveProvider.CreateEnclaveSession( - attestationInfo, - attestationParameters.ClientDiffieHellmanKey, - enclaveSessionParameters, - customData, - customDataLength, - out sqlEnclaveSession, - counter: out _ - ); - - if (sqlEnclaveSession == null) - { - throw SQL.NullEnclaveSessionReturnedFromProvider(enclaveType, enclaveSessionParameters.AttestationUrl); - } - } - } - - internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) - { - GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out _, out customData, out customDataLength, throwIfNull: false); - } - - private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength, bool throwIfNull) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength); - - if (throwIfNull && sqlEnclaveSession == null) - { - throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveSessionParameters.AttestationUrl); - } - } - - internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(enclaveSessionParameters, enclaveSession); - } - - - private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) - { - if (!s_enclaveProviders.TryGetValue(attestationProtocol, out SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider)) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - AzureAttestationEnclaveProvider azureAttestationEnclaveProvider = new AzureAttestationEnclaveProvider(); - s_enclaveProviders[attestationProtocol] = azureAttestationEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; - break; - - case SqlConnectionAttestationProtocol.HGS: - HostGuardianServiceEnclaveProvider hostGuardianServiceEnclaveProvider = new HostGuardianServiceEnclaveProvider(); - s_enclaveProviders[attestationProtocol] = hostGuardianServiceEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; - break; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - SimulatorEnclaveProvider simulatorEnclaveProvider = new SimulatorEnclaveProvider(); - s_enclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; - break; -#endif - - default: - break; - } - } - - if (sqlColumnEncryptionEnclaveProvider == null) - { - throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); - } - - return sqlColumnEncryptionEnclaveProvider; - } - - /// - /// Generate the byte package that needs to be sent to the enclave - /// - /// attestation protocol - /// Keys to be sent to enclave - /// - /// enclave type - /// The set of parameters required for enclave session. - /// connection executing the query - /// - internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysToBeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection) - { - SqlEnclaveSession sqlEnclaveSession; - long counter; - - try - { - GetEnclaveSession( - attestationProtocol, - enclaveType, - enclaveSessionParameters, - generateCustomData: false, - sqlEnclaveSession: out sqlEnclaveSession, - counter: out counter, - customData: out _, - customDataLength: out _, - throwIfNull: true - ); - } - catch (Exception e) - { - throw new RetryableEnclaveQueryExecutionException(e.Message, e); - } - - List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysToBeSentToEnclave, enclaveSessionParameters.ServerName, connection); - byte[] queryStringHashBytes = ComputeQueryStringHash(queryText); - byte[] keyBytePackage = GenerateBytePackageForKeys(counter, queryStringHashBytes, decryptedKeysToBeSentToEnclave); - byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); - byte[] encryptedBytePackage = EncryptBytePackage(keyBytePackage, sessionKey, enclaveSessionParameters.ServerName); - byte[] enclaveSessionHandle = BitConverter.GetBytes(sqlEnclaveSession.SessionId); - byte[] byteArrayToBeSentToEnclave = CombineByteArrays(enclaveSessionHandle, encryptedBytePackage); - return new EnclavePackage(byteArrayToBeSentToEnclave, sqlEnclaveSession); - } - - - internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string attestationUrl, byte[] customData, int customDataLength) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(attestationUrl, customData, customDataLength); - } - - internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParameters sqlEnclaveAttestationParameters, string enclaveType) - { - byte[] attestationProtocolBytes = null; - byte[] attestationProtocolInputLengthBytes = null; - byte[] clientDHPublicKeyLengthBytes = null; - int attestationProtocolInt = sqlEnclaveAttestationParameters.Protocol; - - attestationProtocolBytes = GetUintBytes(enclaveType, attestationProtocolInt, "attestationProtocol"); - - if (attestationProtocolBytes == null) - { - throw SQL.NullArgumentInternal(nameof(attestationProtocolBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); - } - - byte[] attestationProtocolInputBytes = sqlEnclaveAttestationParameters.GetInput(); - - attestationProtocolInputLengthBytes = GetUintBytes(enclaveType, attestationProtocolInputBytes.Length, "attestationProtocolInputLength"); - - if (attestationProtocolInputLengthBytes == null) - { - throw SQL.NullArgumentInternal(nameof(attestationProtocolInputLengthBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); - } - - byte[] clientDHPublicKey = sqlEnclaveAttestationParameters.ClientDiffieHellmanKey.Key.Export(CngKeyBlobFormat.EccPublicBlob); - - clientDHPublicKeyLengthBytes = GetUintBytes(enclaveType, clientDHPublicKey.Length, "clientDHPublicKeyLength"); - - if (clientDHPublicKeyLengthBytes == null) - { - throw SQL.NullArgumentInternal(nameof(clientDHPublicKeyLengthBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); - } - - return CombineByteArrays(attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey); - } - - private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - return "AAS"; - - case SqlConnectionAttestationProtocol.HGS: - return "HGS"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs index 00f05363bc..9017b84717 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs @@ -17,7 +17,7 @@ internal abstract class SqlColumnEncryptionEnclaveProvider internal abstract SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength); /// - internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellmanCng clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter); + internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellman clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter); /// internal abstract void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index aa054ae5a1..e962a5695c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -31,7 +31,8 @@ namespace Microsoft.Data.SqlClient [ DefaultEvent("RecordsAffected"), ToolboxItem(true), - Designer("Microsoft.VSDesigner.Data.VS.SqlCommandDesigner, " + AssemblyRef.MicrosoftVSDesigner) + Designer("Microsoft.VSDesigner.Data.VS.SqlCommandDesigner, " + AssemblyRef.MicrosoftVSDesigner), + DesignerCategory("") ] public sealed class SqlCommand : DbCommand, ICloneable { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs index 0ee215f1fd..11839689f5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs @@ -14,6 +14,7 @@ namespace Microsoft.Data.SqlClient { /// + [DesignerCategory("")] public sealed class SqlCommandBuilder : DbCommandBuilder { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index a7b0c5cfd4..70252f0471 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -35,7 +35,10 @@ namespace Microsoft.Data.SqlClient using Microsoft.Data.Common; /// - [DefaultEvent("InfoMessage")] + [ + DefaultEvent("InfoMessage"), + DesignerCategory("") + ] public sealed partial class SqlConnection : DbConnection, ICloneable { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs index 68da2c6f4e..0e223df8ba 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs @@ -15,7 +15,8 @@ namespace Microsoft.Data.SqlClient [ DefaultEvent("RowUpdated"), ToolboxItem("Microsoft.VSDesigner.Data.VS.SqlDataAdapterToolboxItem, " + AssemblyRef.MicrosoftVSDesigner), - Designer("Microsoft.VSDesigner.Data.VS.SqlDataAdapterDesigner, " + AssemblyRef.MicrosoftVSDesigner) + Designer("Microsoft.VSDesigner.Data.VS.SqlDataAdapterDesigner, " + AssemblyRef.MicrosoftVSDesigner), + DesignerCategory("") ] public sealed class SqlDataAdapter : DbDataAdapter, IDbDataAdapter, ICloneable { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs deleted file mode 100644 index 3422180d8e..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs +++ /dev/null @@ -1,69 +0,0 @@ -// 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.Security.Cryptography; - -namespace Microsoft.Data.SqlClient -{ - - /// - internal class SqlEnclaveAttestationParameters - { - - private static readonly string _clientDiffieHellmanKeyName = "ClientDiffieHellmanKey"; - private static readonly string _inputName = "input"; - private static readonly string _className = "EnclaveAttestationParameters"; - - private readonly byte[] _input; - - /// - internal int Protocol { get; } - - - /// - internal ECDiffieHellmanCng ClientDiffieHellmanKey { get; } - - /// - internal byte[] GetInput() - { - return Clone(_input); - } - - /// - /// Deep copy the array into a new array - /// - /// - /// - private byte[] Clone(byte[] arrayToClone) - { - - if (null == arrayToClone) - { - return null; - } - - byte[] returnValue = new byte[arrayToClone.Length]; - - for (int i = 0; i < arrayToClone.Length; i++) - { - returnValue[i] = arrayToClone[i]; - } - - return returnValue; - } - - /// - internal SqlEnclaveAttestationParameters(int protocol, byte[] input, ECDiffieHellmanCng clientDiffieHellmanKey) - { - if (null == clientDiffieHellmanKey) - { throw SQL.NullArgumentInConstructorInternal(_clientDiffieHellmanKeyName, _className); } - if (null == input) - { throw SQL.NullArgumentInConstructorInternal(_inputName, _className); } - - _input = input; - Protocol = protocol; - ClientDiffieHellmanKey = clientDiffieHellmanKey; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs deleted file mode 100644 index 6f7c62ce66..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ /dev/null @@ -1,367 +0,0 @@ -// 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.Linq; -using System.Runtime.Caching; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Threading; - -namespace Microsoft.Data.SqlClient -{ - internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase - { - #region Members - - private static readonly MemoryCache rootSigningCertificateCache = new MemoryCache("RootSigningCertificateCache"); - - #endregion - - #region Constants - - private const int DiffieHellmanKeySize = 384; - private const int VsmHGSProtocolId = 3; - - // ENCLAVE_IDENTITY related constants - private static readonly EnclaveIdentity ExpectedPolicy = new EnclaveIdentity() - { - OwnerId = new byte[] - { - 0x10, 0x20, 0x30, 0x40, 0x41, 0x31, 0x21, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }, - - UniqueId = new byte[] { }, - - // This field is calculated as follows: - // "Fixed Microsoft GUID" = {845319A6-706C-47BC-A7E8-5137B0BC750D} - // CN of the certificate that signed the file - // Opus Info - Authenticated Attribute that contains a Name and a URL - // - // In our case the Opus Info is: - // Description: Microsoft SQL Server Always Encrypted VBS Enclave Library, - // Description URL: https://go.microsoft.com/fwlink/?linkid=2018716 - AuthorId = new byte[] - { - 0x04, 0x37, 0xCA, 0xE2, 0x53, 0x7D, 0x8B, 0x9B, - 0x07, 0x76, 0xB6, 0x1B, 0x11, 0xE6, 0xCE, 0xD3, - 0xD2, 0x32, 0xE9, 0x30, 0x8F, 0x60, 0xE2, 0x1A, - 0xDA, 0xB2, 0xFD, 0x91, 0xE3, 0xDA, 0x95, 0x98 - }, - - FamilyId = new byte[] - { - 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }, - - ImageId = new byte[] - { - // This value should be same as defined in sqlserver code Sql/Ntdbms/aetm/enclave/dllmain.cpp - 0x19, 0x17, 0x12, 0x00, 0x01, 0x05, 0x20, 0x13, - 0x00, 0x05, 0x14, 0x03, 0x12, 0x01, 0x22, 0x05 - }, - - EnclaveSvn = 0, - - SecureKernelSvn = 0, - - PlatformSvn = 1, - - // 0: ENCLAVE_VBS_FLAG_NO_DEBUG in ds_main; Flag does not permit debug enclaves - Flags = 0, - - SigningLevel = 0, - - Reserved = 0 - }; - - #endregion - - #region Internal methods - - // When overridden in a derived class, looks up an existing enclave session information in the enclave session cache. - // If the enclave provider doesn't implement enclave session caching, this method is expected to return null in the sqlEnclaveSession parameter. - internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) - { - GetEnclaveSessionHelper(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out customData, out customDataLength); - } - - // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) - { - ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); - clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; - clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; - return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, new byte[] { }, clientDHKey); - } - - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) - { - sqlEnclaveSession = null; - counter = 0; - try - { - ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); - if (sqlEnclaveSession == null) - { - if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl)) - { - // Deserialize the payload - AttestationInfo info = new AttestationInfo(attestationInfo); - - // Verify enclave policy matches expected policy - VerifyEnclavePolicy(info.EnclaveReportPackage); - - // Perform Attestation per VSM protocol - VerifyAttestationInfo(enclaveSessionParameters.AttestationUrl, info.HealthReport, info.EnclaveReportPackage); - - // Set up shared secret and validate signature - byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, clientDHKey); - - // add session to cache - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, info.SessionId, out counter); - } - else - { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); - } - } - } - finally - { - UpdateEnclaveSessionLockStatus(sqlEnclaveSession); - } - } - - // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. - internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); - } - - #endregion - - #region Private helpers - - // Performs Attestation per the protocol used by Virtual Secure Modules. - private void VerifyAttestationInfo(string attestationUrl, HealthReport healthReport, EnclaveReportPackage enclaveReportPackage) - { - bool shouldRetryValidation; - bool shouldForceUpdateSigningKeys = false; - do - { - shouldRetryValidation = false; - - // Get HGS Root signing certs from HGS - X509Certificate2Collection signingCerts = GetSigningCertificate(attestationUrl, shouldForceUpdateSigningKeys); - - // Verify SQL Health report root chain of trust is the HGS root signing cert - X509ChainStatusFlags chainStatus = VerifyHealthReportAgainstRootCertificate(signingCerts, healthReport.Certificate); - if (chainStatus != X509ChainStatusFlags.NoError) - { - // In cases if we fail to validate the health report, it might be possible that we are using old signing keys - // let's re-download the signing keys again and re-validate the health report - if (!shouldForceUpdateSigningKeys) - { - shouldForceUpdateSigningKeys = true; - shouldRetryValidation = true; - } - else - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.VerifyHealthCertificateChainFormat, attestationUrl, chainStatus)); - } - } - } while (shouldRetryValidation); - - // Verify enclave report is signed by IDK_S from health report - VerifyEnclaveReportSignature(enclaveReportPackage, healthReport.Certificate); - } - - // Makes a web request to the provided url and returns the response as a byte[] - protected abstract byte[] MakeRequest(string url); - - // Gets the root signing certificate for the provided attestation service. - // If the certificate does not exist in the cache, this will make a call to the - // attestation service's "/signingCertificates" endpoint. This endpoint can - // return multiple certificates if the attestation service consists - // of multiple nodes. - private X509Certificate2Collection GetSigningCertificate(string attestationUrl, bool forceUpdate) - { - attestationUrl = GetAttestationUrl(attestationUrl); - X509Certificate2Collection signingCertificates = (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; - if (forceUpdate || signingCertificates == null || AnyCertificatesExpired(signingCertificates)) - { - byte[] data = MakeRequest(attestationUrl); - var certificateCollection = new X509Certificate2Collection(); - - try - { - certificateCollection.Import(data); - } - catch (CryptographicException exception) - { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.GetAttestationSigningCertificateFailedInvalidCertificate, attestationUrl), exception); - } - - rootSigningCertificateCache.Add(attestationUrl, certificateCollection, DateTime.Now.AddDays(1)); - } - - return (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; - } - - // Return the endpoint for given attestation url - protected abstract string GetAttestationUrl(string attestationUrl); - - // Checks if any certificates in the collection are expired - private bool AnyCertificatesExpired(X509Certificate2Collection certificates) - { - return certificates.OfType().Any(c => c.NotAfter < DateTime.Now); - } - - // Verifies that a chain of trust can be built from the health report provided - // by SQL Server and the attestation service's root signing certificate(s). - private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certificate2Collection signingCerts, X509Certificate2 healthReportCert) - { - var chain = new X509Chain(); - - foreach (var cert in signingCerts) - { - chain.ChainPolicy.ExtraStore.Add(cert); - } - - chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - - if (!chain.Build(healthReportCert)) - { - bool untrustedRoot = false; - - // iterate over the chain status to check why the build failed - foreach (X509ChainStatus status in chain.ChainStatus) - { - if (status.Status == X509ChainStatusFlags.UntrustedRoot) - { - untrustedRoot = true; - } - else - { - return status.Status; - } - } - - // if the chain failed with untrusted root, this could be because the client doesn't have the root cert - // installed. If the chain's untrusted root cert has the same thumbprint as the signing cert, then we - // do trust it. - if (untrustedRoot) - { - // iterate through the certificate chain, starting at the root since it's likely the - // signing certificate is the root - for (int i = 0; i < chain.ChainElements.Count; i++) - { - X509ChainElement element = chain.ChainElements[chain.ChainElements.Count - 1 - i]; - - foreach (X509Certificate2 cert in signingCerts) - { - if (element.Certificate.Thumbprint == cert.Thumbprint) - { - return X509ChainStatusFlags.NoError; - } - } - } - - // in the case where we didn't find matching thumbprint - return X509ChainStatusFlags.UntrustedRoot; - } - } - - return X509ChainStatusFlags.NoError; - } - - // Verifies the enclave report signature using the health report. - private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPackage, X509Certificate2 healthReportCert) - { - // Check if report is formatted correctly - UInt32 calculatedSize = Convert.ToUInt32(enclaveReportPackage.PackageHeader.GetSizeInPayload()) + enclaveReportPackage.PackageHeader.SignedStatementSize + enclaveReportPackage.PackageHeader.SignatureSize; - - if (calculatedSize != enclaveReportPackage.PackageHeader.PackageSize) - { - throw new ArgumentException(Strings.VerifyEnclaveReportFormatFailed); - } - - // IDK_S is contained in healthReport cert public key - RSA rsacsp = healthReportCert.GetRSAPublicKey(); - RSAParameters rsaparams = rsacsp.ExportParameters(includePrivateParameters: false); - RSACng rsacng = new RSACng(); - rsacng.ImportParameters(rsaparams); - - if (!rsacng.VerifyData(enclaveReportPackage.ReportAsBytes, enclaveReportPackage.SignatureBlob, HashAlgorithmName.SHA256, RSASignaturePadding.Pss)) - { - throw new ArgumentException(Strings.VerifyEnclaveReportFailed); - } - } - - // Verifies the enclave policy matches expected policy. - private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) - { - EnclaveIdentity identity = enclaveReportPackage.Report.Identity; - - VerifyEnclavePolicyProperty("OwnerId", identity.OwnerId, ExpectedPolicy.OwnerId); - VerifyEnclavePolicyProperty("AuthorId", identity.AuthorId, ExpectedPolicy.AuthorId); - VerifyEnclavePolicyProperty("FamilyId", identity.FamilyId, ExpectedPolicy.FamilyId); - VerifyEnclavePolicyProperty("ImageId", identity.ImageId, ExpectedPolicy.ImageId); - VerifyEnclavePolicyProperty("EnclaveSvn", identity.EnclaveSvn, ExpectedPolicy.EnclaveSvn); - VerifyEnclavePolicyProperty("SecureKernelSvn", identity.SecureKernelSvn, ExpectedPolicy.SecureKernelSvn); - VerifyEnclavePolicyProperty("PlatformSvn", identity.PlatformSvn, ExpectedPolicy.PlatformSvn); - - // This is a check that the enclave is running without debug support or not. - // - if (identity.Flags != ExpectedPolicy.Flags) - { - throw new InvalidOperationException(Strings.VerifyEnclaveDebuggable); - } - } - - // Verifies a byte[] enclave policy property - private void VerifyEnclavePolicyProperty(string property, byte[] actual, byte[] expected) - { - if (!actual.SequenceEqual(expected)) - { - string exceptionMessage = String.Format(Strings.VerifyEnclavePolicyFailedFormat, property, BitConverter.ToString(actual), BitConverter.ToString(expected)); - throw new ArgumentException(exceptionMessage); - } - } - - // Verifies a uint enclave policy property - private void VerifyEnclavePolicyProperty(string property, uint actual, uint expected) - { - if (actual < expected) - { - string exceptionMessage = String.Format(Strings.VerifyEnclavePolicyFailedFormat, property, actual, expected); - throw new ArgumentException(exceptionMessage); - } - } - - // Derives the shared secret between the client and enclave. - private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) - { - // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. - CngKey cngkey = CngKey.Import(enclavePublicKey.PublicKey, CngKeyBlobFormat.GenericPublicBlob); - RSACng rsacng = new RSACng(cngkey); - if (!rsacng.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) - { - throw new ArgumentException(Strings.GetSharedSecretFailed); - } - - CngKey key = CngKey.Import(enclaveDHInfo.PublicKey, CngKeyBlobFormat.GenericPublicBlob); - return clientDHKey.DeriveKeyMaterial(key); - } - - #endregion - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs similarity index 97% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 0ffa1ad5ce..d08db25036 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -73,9 +73,7 @@ internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSession // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) { - // The key derivation function and hash algorithm name are specified when key derivation is performed - ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); - clientDHKey.KeySize = DiffieHellmanKeySize; + ECDiffieHellman clientDHKey = KeyConverter.CreateECDiffieHellman(DiffieHellmanKeySize); byte[] attestationParam = PrepareAttestationParameters(attestationUrl, customData, customDataLength); return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, clientDHKey); } @@ -528,8 +526,7 @@ private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, } // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. - RSAParameters rsaParams = KeyConverter.RSAPublicKeyBlobToParams(enclaveRsaPublicKey); - using (RSA rsa = RSA.Create(rsaParams)) + using (RSA rsa = KeyConverter.CreateRSAFromPublicKeyBlob(enclaveRsaPublicKey)) { if (!rsa.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) { @@ -537,9 +534,10 @@ private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, } } - ECParameters ecParams = KeyConverter.ECCPublicKeyBlobToParams(enclaveDHInfo.PublicKey); - ECDiffieHellman enclaveDHKey = ECDiffieHellman.Create(ecParams); - return clientDHKey.DeriveKeyFromHash(enclaveDHKey.PublicKey, HashAlgorithmName.SHA256); + using (ECDiffieHellman enclaveDHKey = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(enclaveDHInfo.PublicKey)) + { + return KeyConverter.DeriveKey(clientDHKey, enclaveDHKey.PublicKey); + } } #endregion } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs similarity index 98% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs index 98ba7dd83d..21c91725a5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs @@ -195,7 +195,7 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete throw SQL.NullArgumentInternal(nameof(attestationProtocolInputLengthBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); } - byte[] clientDHPublicKey = KeyConverter.ECDHPublicKeyToECCKeyBlob(sqlEnclaveAttestationParameters.ClientDiffieHellmanKey.PublicKey); + byte[] clientDHPublicKey = KeyConverter.GetECDiffieHellmanPublicKeyBlob(sqlEnclaveAttestationParameters.ClientDiffieHellmanKey); clientDHPublicKeyLengthBytes = GetUintBytes(enclaveType, clientDHPublicKey.Length, "clientDHPublicKeyLength"); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.Crypto.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.Crypto.cs new file mode 100644 index 0000000000..06262b364f --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.Crypto.cs @@ -0,0 +1,52 @@ +// 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.Security.Cryptography; + +namespace Microsoft.Data.SqlClient +{ + /// + internal class SqlEnclaveAttestationParameters + { + private readonly byte[] _input; + + /// + internal SqlEnclaveAttestationParameters(int protocol, byte[] input, ECDiffieHellman clientDiffieHellmanKey) + { + if (input == null) + { + throw SQL.NullArgumentInConstructorInternal(nameof(input), nameof(SqlEnclaveAttestationParameters)); + } + if (clientDiffieHellmanKey == null) + { + throw SQL.NullArgumentInConstructorInternal(nameof(clientDiffieHellmanKey), nameof(SqlEnclaveAttestationParameters)); + } + + _input = input; + Protocol = protocol; + ClientDiffieHellmanKey = clientDiffieHellmanKey; + } + + /// + internal int Protocol { get; private set; } + + /// + internal ECDiffieHellman ClientDiffieHellmanKey { get; private set; } + + /// + internal byte[] GetInput() + { + // return a new array for safety so the caller cannot mutate the original + if (_input == null) + { + return null; + } + + byte[] output = new byte[_input.Length]; + Buffer.BlockCopy(_input, 0, output, 0, _input.Length); + return output; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NotSupported.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NotSupported.cs new file mode 100644 index 0000000000..0ae67d4a94 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NotSupported.cs @@ -0,0 +1,19 @@ +// 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 +{ + /// + internal partial class SqlEnclaveAttestationParameters + { + /// + internal int Protocol { get; } + + /// + internal byte[] GetInput() + { + return null; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs similarity index 93% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 663b4a4e19..f71047d965 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -94,10 +94,7 @@ internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSession // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) { - // The key derivation function and hash algorithm name are specified when key derivation is performed - ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); - clientDHKey.KeySize = DiffieHellmanKeySize; - + ECDiffieHellman clientDHKey = KeyConverter.CreateECDiffieHellman(DiffieHellmanKeySize); return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, new byte[] { }, clientDHKey); } @@ -288,21 +285,19 @@ private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certif private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPackage, X509Certificate2 healthReportCert) { // Check if report is formatted correctly - UInt32 calculatedSize = Convert.ToUInt32(enclaveReportPackage.PackageHeader.GetSizeInPayload()) + enclaveReportPackage.PackageHeader.SignedStatementSize + enclaveReportPackage.PackageHeader.SignatureSize; + uint calculatedSize = Convert.ToUInt32(enclaveReportPackage.PackageHeader.GetSizeInPayload()) + enclaveReportPackage.PackageHeader.SignedStatementSize + enclaveReportPackage.PackageHeader.SignatureSize; if (calculatedSize != enclaveReportPackage.PackageHeader.PackageSize) { throw new ArgumentException(Strings.VerifyEnclaveReportFormatFailed); } - // IDK_S is contained in healthReport cert public key - using (RSA rsa = healthReportCert.GetRSAPublicKey()) + using (RSA rsa = KeyConverter.GetRSAFromCertificate(healthReportCert)) { if (!rsa.VerifyData(enclaveReportPackage.ReportAsBytes, enclaveReportPackage.SignatureBlob, HashAlgorithmName.SHA256, RSASignaturePadding.Pss)) { throw new ArgumentException(Strings.VerifyEnclaveReportFailed); } - } } @@ -342,7 +337,7 @@ private void VerifyEnclavePolicyProperty(string property, uint actual, uint expe { if (actual < expected) { - string exceptionMessage = String.Format(Strings.VerifyEnclavePolicyFailedFormat, property, actual, expected); + string exceptionMessage = string.Format(Strings.VerifyEnclavePolicyFailedFormat, property, actual, expected); throw new ArgumentException(exceptionMessage); } } @@ -351,8 +346,7 @@ private void VerifyEnclavePolicyProperty(string property, uint actual, uint expe private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellman clientDHKey) { // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. - RSAParameters rsaParams = KeyConverter.RSAPublicKeyBlobToParams(enclavePublicKey.PublicKey); - using (RSA rsa = RSA.Create(rsaParams)) + using (RSA rsa = KeyConverter.CreateRSAFromPublicKeyBlob(enclavePublicKey.PublicKey)) { if (!rsa.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) { @@ -360,9 +354,10 @@ private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieH } } - ECParameters ecParams = KeyConverter.ECCPublicKeyBlobToParams(enclaveDHInfo.PublicKey); - ECDiffieHellman enclaveDHKey = ECDiffieHellman.Create(ecParams); - return clientDHKey.DeriveKeyFromHash(enclaveDHKey.PublicKey, HashAlgorithmName.SHA256); + using (ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(enclaveDHInfo.PublicKey)) + { + return KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); + } } #endregion } From 2f19bc4d8db3b18bec61ad6f83d4ab7bb9b7b9f7 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Wed, 28 Apr 2021 12:43:07 -0700 Subject: [PATCH 20/87] add RegisterColumnEncryptionKeyStoreProvidersOnConnection (#1045) --- .../SqlConnection.xml | 18 +++++++ .../netcore/ref/Microsoft.Data.SqlClient.cs | 4 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 35 ++++++++++++ .../netfx/ref/Microsoft.Data.SqlClient.cs | 2 + .../Microsoft/Data/SqlClient/SqlConnection.cs | 32 +++++++++++ .../ExceptionRegisterKeyStoreProvider.cs | 54 +++++++++++++++++++ .../ManualTests/AlwaysEncrypted/ApiShould.cs | 22 ++++++++ 7 files changed, 166 insertions(+), 1 deletion(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 4cc4448f8b..7eee99980d 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -1052,6 +1052,24 @@ GO This function was called more than once. + + Dictionary of custom column encryption key providers + Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the static methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + + A null dictionary was provided. + + -or- + + A string key in the dictionary was null or empty. + + -or- + + An EncryptionKeyStoreProvider value in the dictionary was null. + + + A string key in the dictionary started with "MSSQL_". This prefix is reserved for system providers. + + Gets or sets a value that specifies the diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index b6956df25b..e630a03acb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -689,7 +689,9 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys public static System.Collections.Generic.IDictionary> ColumnEncryptionTrustedMasterKeyPaths { get { throw null; } } /// public static void RegisterColumnEncryptionKeyStoreProviders(System.Collections.Generic.IDictionary customProviders) { } - /// + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(System.Collections.Generic.IDictionary customProviders) { } + /// [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public string AccessToken { get { throw null; } set { } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 33fd700673..7cdd3b56d5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -91,6 +91,11 @@ private enum CultureCheckState : uint /// private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; + /// + /// Per-connection custom providers. It can be provided by the user and can be set more than once. + /// + private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + /// /// Dictionary object holding trusted key paths for various SQL Servers. /// Key to the dictionary is a SQL Server Name @@ -234,6 +239,13 @@ internal static bool TryGetColumnEncryptionKeyStoreProvider(string providerName, return true; } + // instance-level custom provider cache takes precedence over global cache + if (connection._customColumnEncryptionKeyStoreProviders != null && + connection._customColumnEncryptionKeyStoreProviders.Count > 0) + { + return connection._customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); + } + lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false @@ -264,6 +276,11 @@ internal static List GetColumnEncryptionSystemKeyStoreProviders() /// Combined list of provider names internal static List GetColumnEncryptionCustomKeyStoreProviders(SqlConnection connection) { + if (connection._customColumnEncryptionKeyStoreProviders != null && + connection._customColumnEncryptionKeyStoreProviders.Count > 0) + { + return connection._customColumnEncryptionKeyStoreProviders.Keys.ToList(); + } if (s_globalCustomColumnEncryptionKeyStoreProviders != null) { return s_globalCustomColumnEncryptionKeyStoreProviders.Keys.ToList(); @@ -306,6 +323,24 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) + { + ValidateCustomProviders(customProviders); + + // Create a temporary dictionary and then add items from the provided dictionary. + // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs + // in the provided customerProviders dictionary. + Dictionary customColumnEncryptionKeyStoreProviders = + new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); + + // Set the dictionary to the ReadOnly dictionary. + // This method can be called more than once. Re-registering a new collection will replace the + // old collection of providers. + _customColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; + } + private static void ValidateCustomProviders(IDictionary customProviders) { // Throw when the provided dictionary is null. diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 16227cc0c8..23c88ff5b5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -850,6 +850,8 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys public override System.Threading.Tasks.Task OpenAsync(System.Threading.CancellationToken cancellationToken) { throw null; } /// public static void RegisterColumnEncryptionKeyStoreProviders(System.Collections.Generic.IDictionary customProviders) { } + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(System.Collections.Generic.IDictionary customProviders) { } /// public void ResetStatistics() { } /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 70252f0471..f0ff39a09e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -67,6 +67,9 @@ internal bool ForceNewConnection /// private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; + /// Instance-level list of custom key store providers. It can be set more than once by the user. + private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + // Lock to control setting of s_globalCustomColumnEncryptionKeyStoreProviders private static readonly object s_globalCustomColumnEncryptionKeyProvidersLock = new object(); @@ -164,6 +167,23 @@ static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) + { + ValidateCustomProviders(customProviders); + + // Create a temporary dictionary and then add items from the provided dictionary. + // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs + // in the provided customerProviders dictionary. + Dictionary customColumnEncryptionKeyStoreProviders = + new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); + + // Set the dictionary to the ReadOnly dictionary. + // This method can be called more than once. Re-registering a new collection will replace the + // old collection of providers. + _customColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; + } + private static void ValidateCustomProviders(IDictionary customProviders) { // Throw when the provided dictionary is null. @@ -216,6 +236,13 @@ static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, return true; } + // instance-level custom provider cache takes precedence over global cache + if (connection._customColumnEncryptionKeyStoreProviders != null && + connection._customColumnEncryptionKeyStoreProviders.Count > 0) + { + return connection._customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); + } + lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false @@ -246,6 +273,11 @@ internal static List GetColumnEncryptionSystemKeyStoreProviders() /// Combined list of provider names internal static List GetColumnEncryptionCustomKeyStoreProviders(SqlConnection connection) { + if (connection._customColumnEncryptionKeyStoreProviders != null && + connection._customColumnEncryptionKeyStoreProviders.Count > 0) + { + return connection._customColumnEncryptionKeyStoreProviders.Keys.ToList(); + } if (s_globalCustomColumnEncryptionKeyStoreProviders != null) { return s_globalCustomColumnEncryptionKeyStoreProviders.Keys.ToList(); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs index 65f875f9ce..d703d4f748 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs @@ -22,6 +22,9 @@ public void TestNullDictionary() ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); Assert.Contains(expectedMessage, e.Message); + + e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); + Assert.Contains(expectedMessage, e.Message); } [Fact] @@ -35,6 +38,9 @@ public void TestInvalidProviderName() ArgumentException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); Assert.Contains(expectedMessage, e.Message); + + e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); + Assert.Contains(expectedMessage, e.Message); } [Fact] @@ -48,6 +54,9 @@ public void TestNullProviderValue() ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); Assert.Contains(expectedMessage, e.Message); + + e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); + Assert.Contains(expectedMessage, e.Message); } [Fact] @@ -60,6 +69,9 @@ public void TestEmptyProviderName() ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); Assert.Contains(expectedMessage, e.Message); + + e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); + Assert.Contains(expectedMessage, e.Message); } [Fact] @@ -81,5 +93,47 @@ public void TestCanSetGlobalProvidersOnlyOnce() Utility.ClearSqlConnectionGlobalProviders(); } + + [Fact] + public void TestCanSetInstanceProvidersMoreThanOnce() + { + const string dummyProviderName1 = "DummyProvider1"; + const string dummyProviderName2 = "DummyProvider2"; + const string dummyProviderName3 = "DummyProvider3"; + IDictionary singleKeyStoreProvider = + new Dictionary() + { + {dummyProviderName1, new DummyKeyStoreProvider() } + }; + + IDictionary multipleKeyStoreProviders = + new Dictionary() + { + { dummyProviderName2, new DummyKeyStoreProvider() }, + { dummyProviderName3, new DummyKeyStoreProvider() } + }; + + using (SqlConnection connection = new SqlConnection()) + { + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(singleKeyStoreProvider); + IReadOnlyDictionary instanceCache = + GetInstanceCacheFromConnection(connection); + Assert.Single(instanceCache); + Assert.True(instanceCache.ContainsKey(dummyProviderName1)); + + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(multipleKeyStoreProviders); + instanceCache = GetInstanceCacheFromConnection(connection); + Assert.Equal(2, instanceCache.Count); + Assert.True(instanceCache.ContainsKey(dummyProviderName2)); + Assert.True(instanceCache.ContainsKey(dummyProviderName3)); + } + + IReadOnlyDictionary GetInstanceCacheFromConnection(SqlConnection conn) + { + FieldInfo instanceCacheField = conn.GetType().GetField( + "_customColumnEncryptionKeyStoreProviders", BindingFlags.NonPublic | BindingFlags.Instance); + return instanceCacheField.GetValue(conn) as IReadOnlyDictionary; + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index b5cc271605..30e24f8960 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -2160,6 +2160,28 @@ public void TestCustomKeyStoreProviderDuringAeQuery(string connectionString) () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); Assert.Contains(failedToDecryptMessage, ex.Message); Assert.True(ex.InnerException is NotImplementedException); + + // not required provider in instance cache + // it should not fall back to the global cache so the right provider will not be found + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(notRequiredProvider); + ex = Assert.Throws( + () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); + Assert.Equal(providerNotFoundMessage, ex.Message); + + // required provider in instance cache + // if the instance cache is not empty, it is always checked for the provider. + // => if the provider is found, it must have been retrieved from the instance cache and not the global cache + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(requiredProvider); + ex = Assert.Throws( + () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); + Assert.Contains(failedToDecryptMessage, ex.Message); + Assert.True(ex.InnerException is NotImplementedException); + + // not required provider will replace the previous entry so required provider will not be found + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(notRequiredProvider); + ex = Assert.Throws( + () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); + Assert.Equal(providerNotFoundMessage, ex.Message); } void ExecuteQueryThatRequiresCustomKeyStoreProvider(SqlConnection connection) From ff821cf0b4dfbcc2666a0c6916145235500047d1 Mon Sep 17 00:00:00 2001 From: Wraith Date: Wed, 28 Apr 2021 22:15:00 +0100 Subject: [PATCH 21/87] Share SqlCredential (#1038) --- .../src/Microsoft.Data.SqlClient.csproj | 4 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../Microsoft/Data/SqlClient/SqlCredential.cs | 76 ------------------- .../Microsoft/Data/SqlClient/SqlCredential.cs | 8 +- 4 files changed, 10 insertions(+), 82 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCredential.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/SqlCredential.cs (68%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 1b74587f24..6ef32b56c6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -214,6 +214,9 @@ Microsoft\Data\SqlClient\SqlConnectionPoolProviderInfo.cs + + Microsoft\Data\SqlClient\SqlCredential.cs + Microsoft\Data\SqlClient\SqlInfoMessageEventHandler.cs @@ -530,7 +533,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 1ad37256a2..d539214f0b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -454,7 +454,9 @@ - + + Microsoft\Data\SqlClient\SqlCredential.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCredential.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCredential.cs deleted file mode 100644 index 93ae8f12ed..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCredential.cs +++ /dev/null @@ -1,76 +0,0 @@ -// 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.Security; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - /// - // Represent a pair of user id and password which to be used for SQL Authentication - // SqlCredential takes password as SecureString which is better way to store security sensitive information - // This class is immutable - public sealed class SqlCredential - { - string _userId; - SecureString _password; - - /// - // PUBLIC CONSTRUCTOR - // SqlCredential - // userId: userId - // password: password - public SqlCredential(string userId, SecureString password) - { - if (userId == null) - { - throw ADP.ArgumentNull("userId"); - } - - if (userId.Length > TdsEnums.MAXLEN_CLIENTID) - { - throw ADP.InvalidArgumentLength("userId", TdsEnums.MAXLEN_CLIENTID); - } - - if (password == null) - { - throw ADP.ArgumentNull("password"); - } - - if (password.Length > TdsEnums.MAXLEN_CLIENTSECRET) - { - throw ADP.InvalidArgumentLength("password", TdsEnums.MAXLEN_CLIENTSECRET); - } - - if (!password.IsReadOnly()) - { - throw ADP.MustBeReadOnly("password"); - } - - _userId = userId; - _password = password; - } - - /// - // PUBLIC PROPERTIES - public string UserId - { - get - { - return _userId; - } - } - - /// - public SecureString Password - { - get - { - return _password; - } - } - } -} // Microsoft.Data.SqlClient namespace - - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCredential.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCredential.cs similarity index 68% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCredential.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCredential.cs index 5ad7821352..3859b0b000 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCredential.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCredential.cs @@ -7,13 +7,13 @@ namespace Microsoft.Data.SqlClient { - /// + /// public sealed class SqlCredential { string _userId; SecureString _password; - /// + /// public SqlCredential(string userId, SecureString password) { if (userId == null) @@ -45,10 +45,10 @@ public SqlCredential(string userId, SecureString password) _password = password; } - /// + /// public string UserId => _userId; - /// + /// public SecureString Password => _password; } From 4f3397d4a1b3751f654aa460e5c6317b689b0ea8 Mon Sep 17 00:00:00 2001 From: Wraith Date: Wed, 28 Apr 2021 22:15:22 +0100 Subject: [PATCH 22/87] Share SqlUdtInfo (#1040) --- .../src/Microsoft.Data.SqlClient.csproj | 4 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../Microsoft/Data/SqlClient/SqlUdtInfo.cs | 68 ------------------- .../Microsoft/Data/SqlClient/SqlUdtInfo.cs | 5 +- 4 files changed, 9 insertions(+), 72 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs (95%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 6ef32b56c6..a309305dc3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -498,7 +498,9 @@ - + + Microsoft\Data\SqlClient\SqlUdtInfo.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index d539214f0b..914fe96939 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -485,7 +485,9 @@ - + + Microsoft\Data\SqlClient\SqlUdtInfo.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs deleted file mode 100644 index 11d24a0a27..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs +++ /dev/null @@ -1,68 +0,0 @@ -// 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.Generic; -using System.Reflection; -using Microsoft.Data.SqlClient.Server; - -namespace Microsoft.Data.SqlClient -{ - internal class SqlUdtInfo - { - internal readonly Format SerializationFormat; - internal readonly bool IsByteOrdered; - internal readonly bool IsFixedLength; - internal readonly int MaxByteSize; - internal readonly string Name; - internal readonly string ValidationMethodName; - - private SqlUdtInfo(SqlUserDefinedTypeAttribute attr) - { - SerializationFormat = (Format)attr.Format; - IsByteOrdered = attr.IsByteOrdered; - IsFixedLength = attr.IsFixedLength; - MaxByteSize = attr.MaxByteSize; - Name = attr.Name; - ValidationMethodName = attr.ValidationMethodName; - } - internal static SqlUdtInfo GetFromType(Type target) - { - SqlUdtInfo udtAttr = TryGetFromType(target); - if (udtAttr == null) - { - Type myType = typeof(InvalidUdtException); - var arguments = new Type[] { typeof(Type), typeof(String) }; - MethodInfo Create = myType.GetMethod("Create", arguments); - Create.Invoke(null, new object[] { Strings.SqlUdtReason_NoUdtAttribute }); - } - return udtAttr; - } - - // VSTFDEVDIV 479671: Type.GetCustomAttributes is an time-expensive call. - // Improve UDT serialization performance by caching the resulted UDT type information using type-safe dictionary. - // Use a per-thread cache, so we do not need to synchronize access to it - [ThreadStatic] - private static Dictionary m_types2UdtInfo; - - internal static SqlUdtInfo TryGetFromType(Type target) - { - if (m_types2UdtInfo == null) - m_types2UdtInfo = new Dictionary(); - - SqlUdtInfo udtAttr = null; - if (!m_types2UdtInfo.TryGetValue(target, out udtAttr)) - { - // query SqlUserDefinedTypeAttribute first time and cache the result - object[] attr = target.GetCustomAttributes(typeof(SqlUserDefinedTypeAttribute), false); - if (attr != null && attr.Length == 1) - { - udtAttr = new SqlUdtInfo((SqlUserDefinedTypeAttribute)attr[0]); - } - m_types2UdtInfo.Add(target, udtAttr); - } - return udtAttr; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs similarity index 95% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs index 7a02ba83fd..c87051dd8a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs @@ -45,10 +45,11 @@ internal static SqlUdtInfo GetFromType(Type target) internal static SqlUdtInfo TryGetFromType(Type target) { if (s_types2UdtInfo == null) + { s_types2UdtInfo = new Dictionary(); + } - SqlUdtInfo udtAttr = null; - if (!s_types2UdtInfo.TryGetValue(target, out udtAttr)) + if (!s_types2UdtInfo.TryGetValue(target, out SqlUdtInfo udtAttr)) { // query SqlUserDefinedTypeAttribute first time and cache the result object[] attr = target.GetCustomAttributes(typeof(SqlUserDefinedTypeAttribute), false); From d8fd754e0b6f70787a7959c5b732a880b641e4e1 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 30 Apr 2021 14:08:36 -0700 Subject: [PATCH 23/87] Update Language version to 9.0 (#1055) --- src/Directory.Build.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 8b0b9d42fd..6acb394869 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,7 @@ + 9.0 true false true From 1eabe061ddc231f33abeaa3210ad6bf5a7d52ee6 Mon Sep 17 00:00:00 2001 From: Wraith Date: Fri, 7 May 2021 05:44:24 +0100 Subject: [PATCH 24/87] Shrink SqlParameter XmlSchema (#1033) --- .../Microsoft/Data/SqlClient/SqlParameter.cs | 57 ++++++++++------- .../Microsoft/Data/SqlClient/SqlParameter.cs | 63 +++++++++--------- .../tests/FunctionalTests/SqlParameterTest.cs | 64 +++++++++++++++++++ 3 files changed, 131 insertions(+), 53 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs index 2bab7e4dbd..e01a5560ff 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -210,9 +210,7 @@ private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) private MetaType _metaType; private SqlCollation _collation; - private string _xmlSchemaCollectionDatabase; - private string _xmlSchemaCollectionOwningSchema; - private string _xmlSchemaCollectionName; + private SqlMetaDataXmlSchemaCollection _xmlSchemaCollection; private string _udtTypeName; private string _typeName; private Exception _udtLoadError; @@ -244,6 +242,7 @@ public SqlParameter() : base() { _isNull = true; _actualSize = -1; + _direction = ParameterDirection.Input; } /// @@ -331,9 +330,13 @@ string xmlSchemaCollectionName SourceVersion = sourceVersion; SourceColumnNullMapping = sourceColumnNullMapping; Value = value; - XmlSchemaCollectionDatabase = xmlSchemaCollectionDatabase; - XmlSchemaCollectionOwningSchema = xmlSchemaCollectionOwningSchema; - XmlSchemaCollectionName = xmlSchemaCollectionName; + if (!string.IsNullOrEmpty(xmlSchemaCollectionDatabase) || !string.IsNullOrEmpty(xmlSchemaCollectionOwningSchema) || !string.IsNullOrEmpty(xmlSchemaCollectionName)) + { + EnsureXmlSchemaCollection(); + _xmlSchemaCollection.Database = xmlSchemaCollectionDatabase; + _xmlSchemaCollection.OwningSchema = xmlSchemaCollectionOwningSchema; + _xmlSchemaCollection.Name = xmlSchemaCollectionName; + } } private SqlParameter(SqlParameter source) : this() @@ -404,24 +407,24 @@ public SqlCompareOptions CompareInfo [ResCategory("XML")] public string XmlSchemaCollectionDatabase { - get => _xmlSchemaCollectionDatabase ?? ADP.StrEmpty; - set => _xmlSchemaCollectionDatabase = value; + get => _xmlSchemaCollection?.Database ?? string.Empty; + set => EnsureXmlSchemaCollection().Database = value; } /// [ResCategory("XML")] public string XmlSchemaCollectionOwningSchema { - get => _xmlSchemaCollectionOwningSchema ?? ADP.StrEmpty; - set => _xmlSchemaCollectionOwningSchema = value; + get => _xmlSchemaCollection?.OwningSchema ?? string.Empty; + set => EnsureXmlSchemaCollection().OwningSchema = value; } /// [ResCategory("XML")] public string XmlSchemaCollectionName { - get => _xmlSchemaCollectionName ?? ADP.StrEmpty; - set => _xmlSchemaCollectionName = value; + get => _xmlSchemaCollection?.Name ?? string.Empty; + set => EnsureXmlSchemaCollection().Name = value; } /// @@ -452,7 +455,7 @@ public override DbType DbType /// public override string ParameterName { - get => _parameterName ?? ADP.StrEmpty; + get => _parameterName ?? string.Empty; set { if ( @@ -657,7 +660,7 @@ public object SqlValue ] public string UdtTypeName { - get => _udtTypeName ?? ADP.StrEmpty; + get => _udtTypeName ?? string.Empty; set => _udtTypeName = value; } @@ -668,7 +671,7 @@ public string UdtTypeName ] public string TypeName { - get => _typeName ?? ADP.StrEmpty; + get => _typeName ?? string.Empty; set { _typeName = value; @@ -725,11 +728,7 @@ public override object Value ] public override ParameterDirection Direction { - get - { - ParameterDirection direction = _direction; - return (direction != 0) ? direction : ParameterDirection.Input; - } + get => _direction; set { if (_direction != value) @@ -813,7 +812,7 @@ private void ResetSize() [ResCategory("Update")] public override string SourceColumn { - get => _sourceColumn ?? ADP.StrEmpty; + get => _sourceColumn ?? string.Empty; set => _sourceColumn = value; } @@ -984,9 +983,10 @@ private void CloneHelper(SqlParameter destination) destination._metaType = _metaType; destination._collation = _collation; - destination._xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase; - destination._xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema; - destination._xmlSchemaCollectionName = _xmlSchemaCollectionName; + if (_xmlSchemaCollection != null) + { + destination.EnsureXmlSchemaCollection().CopyFrom(_xmlSchemaCollection); + } destination._udtTypeName = _udtTypeName; destination._typeName = _typeName; destination._udtLoadError = _udtLoadError; @@ -1022,6 +1022,15 @@ internal object CompareExchangeParent(object value, object comparand) return parent; } + private SqlMetaDataXmlSchemaCollection EnsureXmlSchemaCollection() + { + if (_xmlSchemaCollection is null) + { + _xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + } + return _xmlSchemaCollection; + } + internal void FixStreamDataForNonPLP() { object value = GetCoercedValue(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs index 26174a9b67..c0a2e54021 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -193,9 +193,7 @@ private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) private MetaType _metaType; private SqlCollation _collation; - private string _xmlSchemaCollectionDatabase; - private string _xmlSchemaCollectionOwningSchema; - private string _xmlSchemaCollectionName; + private SqlMetaDataXmlSchemaCollection _xmlSchemaCollection; private string _udtTypeName; private string _typeName; private Exception _udtLoadError; @@ -227,6 +225,7 @@ public SqlParameter() : base() { _isNull = true; _actualSize = -1; + _direction = ParameterDirection.Input; } /// @@ -276,16 +275,12 @@ public SqlParameter(string parameterName, SqlDbType dbType, int size, string sou DataRowVersion sourceVersion, object value ) - : this() + : this(parameterName, dbType, size, sourceColumn) { - ParameterName = parameterName; - SqlDbType = dbType; - Size = size; Direction = direction; IsNullable = isNullable; PrecisionInternal = precision; ScaleInternal = scale; - SourceColumn = sourceColumn; SourceVersion = sourceVersion; Value = value; } @@ -318,9 +313,13 @@ string xmlSchemaCollectionName SourceVersion = sourceVersion; SourceColumnNullMapping = sourceColumnNullMapping; Value = value; - _xmlSchemaCollectionDatabase = xmlSchemaCollectionDatabase; - _xmlSchemaCollectionOwningSchema = xmlSchemaCollectionOwningSchema; - _xmlSchemaCollectionName = xmlSchemaCollectionName; + if (!string.IsNullOrEmpty(xmlSchemaCollectionDatabase) || !string.IsNullOrEmpty(xmlSchemaCollectionOwningSchema) || !string.IsNullOrEmpty(xmlSchemaCollectionName)) + { + EnsureXmlSchemaCollection(); + _xmlSchemaCollection.Database = xmlSchemaCollectionDatabase; + _xmlSchemaCollection.OwningSchema = xmlSchemaCollectionOwningSchema; + _xmlSchemaCollection.Name = xmlSchemaCollectionName; + } } private SqlParameter(SqlParameter source) : this() @@ -391,24 +390,24 @@ public SqlCompareOptions CompareInfo [ResCategory("XML")] public string XmlSchemaCollectionDatabase { - get => _xmlSchemaCollectionDatabase ?? ADP.StrEmpty; - set => _xmlSchemaCollectionDatabase = value; + get => _xmlSchemaCollection?.Database ?? string.Empty; + set => EnsureXmlSchemaCollection().Database = value; } /// [ResCategory("XML")] public string XmlSchemaCollectionOwningSchema { - get => _xmlSchemaCollectionOwningSchema ?? ADP.StrEmpty; - set => _xmlSchemaCollectionOwningSchema = value; + get => _xmlSchemaCollection?.OwningSchema ?? string.Empty; + set => EnsureXmlSchemaCollection().OwningSchema = value; } /// [ResCategory("XML")] public string XmlSchemaCollectionName { - get => _xmlSchemaCollectionName ?? ADP.StrEmpty; - set => _xmlSchemaCollectionName = value; + get => _xmlSchemaCollection?.Name ?? string.Empty; + set => EnsureXmlSchemaCollection().Name = value; } /// @@ -439,7 +438,7 @@ public override DbType DbType /// public override string ParameterName { - get => _parameterName ?? ADP.StrEmpty; + get => _parameterName ?? string.Empty; set { if ( @@ -644,7 +643,7 @@ public object SqlValue ] public string UdtTypeName { - get => _udtTypeName ?? ADP.StrEmpty; + get => _udtTypeName ?? string.Empty; set => _udtTypeName = value; } @@ -655,7 +654,7 @@ public string UdtTypeName ] public string TypeName { - get => _typeName ?? ADP.StrEmpty; + get => _typeName ?? string.Empty; set { _typeName = value; @@ -712,11 +711,7 @@ public override object Value ] public override ParameterDirection Direction { - get - { - ParameterDirection direction = _direction; - return (direction != 0) ? direction : ParameterDirection.Input; - } + get => _direction; set { if (_direction != value) @@ -800,7 +795,7 @@ private void ResetSize() [ResCategory("Update")] public override string SourceColumn { - get => _sourceColumn ?? ADP.StrEmpty; + get => _sourceColumn ?? string.Empty; set => _sourceColumn = value; } @@ -975,9 +970,10 @@ private void CloneHelper(SqlParameter destination) destination._metaType = _metaType; destination._collation = _collation; - destination._xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase; - destination._xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema; - destination._xmlSchemaCollectionName = _xmlSchemaCollectionName; + if (_xmlSchemaCollection != null) + { + destination.EnsureXmlSchemaCollection().CopyFrom(_xmlSchemaCollection); + } destination._udtTypeName = _udtTypeName; destination._typeName = _typeName; destination._udtLoadError = _udtLoadError; @@ -1017,6 +1013,15 @@ internal object CompareExchangeParent(object value, object comparand) return parent; } + private SqlMetaDataXmlSchemaCollection EnsureXmlSchemaCollection() + { + if (_xmlSchemaCollection is null) + { + _xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + } + return _xmlSchemaCollection; + } + internal void FixStreamDataForNonPLP() { object value = GetCoercedValue(); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs index 203fcc5a65..344a578b4a 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs @@ -1612,6 +1612,70 @@ public void XmlSchemaTest() Assert.Equal(" a ", p1.XmlSchemaCollectionOwningSchema); } + [Fact] + public void CreateParameterWithValidXmlSchema() + { + string xmlDatabase = "database"; + string xmlSchema = "schema"; + string xmlName = "name"; + + SqlParameter parameter = new SqlParameter("@name", SqlDbType.Int, 4, ParameterDirection.Input, 0, 0, "name", DataRowVersion.Original, false, 1, xmlDatabase, xmlSchema, xmlName); + + Assert.Equal(xmlDatabase, parameter.XmlSchemaCollectionDatabase); + Assert.Equal(xmlSchema, parameter.XmlSchemaCollectionOwningSchema); + Assert.Equal(xmlName, parameter.XmlSchemaCollectionName); + } + + [Fact] + public void CreateParameterWithEmptyXmlSchema() + { + SqlParameter parameter = new SqlParameter("@name", SqlDbType.Int, 4, ParameterDirection.Input, 0, 0, "name", DataRowVersion.Original, false, 1, string.Empty, string.Empty, string.Empty); + + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionDatabase); + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionOwningSchema); + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionName); + } + + [Fact] + public void CreateParameterWithNullXmlSchema() + { + SqlParameter parameter = new SqlParameter("@name", SqlDbType.Int, 4, ParameterDirection.Input, 0, 0, "name", DataRowVersion.Original, false, 1, null, null, null); + + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionDatabase); + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionOwningSchema); + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionName); + } + + [Fact] + public void CreateParameterWithoutXmlSchema() + { + SqlParameter parameter = new SqlParameter(); + + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionDatabase); + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionOwningSchema); + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionName); + } + + [Fact] + public void SetParameterXmlSchema() + { + SqlParameter parameter = new SqlParameter(); + + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionName); + + // verify that if we set it to null we still get an empty string back + parameter.XmlSchemaCollectionName = null; + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionName); + + // verify that if we set a value we get it back + parameter.XmlSchemaCollectionName = "name"; + Assert.Equal("name", parameter.XmlSchemaCollectionName); + + // verify that if we set it explicitly to null it reverts to empty string + parameter.XmlSchemaCollectionName = null; + Assert.Equal(string.Empty, parameter.XmlSchemaCollectionName); + } + private enum ByteEnum : byte { A = 0x0a, From 91c5844231c4732936d80005a4605613158f3917 Mon Sep 17 00:00:00 2001 From: Wraith Date: Fri, 7 May 2021 05:44:48 +0100 Subject: [PATCH 25/87] Share SqlStatistics (#1028) --- .../src/Microsoft.Data.SqlClient.csproj | 4 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +- .../Microsoft/Data/SqlClient/SqlStatistics.cs | 221 ------------------ .../netfx/src/Resources/Strings.Designer.cs | 36 +++ .../netfx/src/Resources/Strings.resx | 14 +- .../Microsoft/Data/SqlClient/SqlStatistics.cs | 50 ++-- 7 files changed, 89 insertions(+), 244 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/SqlStatistics.cs (91%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index a309305dc3..d881f7b2b8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -495,7 +495,9 @@ - + + Microsoft\Data\SqlClient\SqlStatistics.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 914fe96939..37e921f1d0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -482,7 +482,9 @@ - + + Microsoft\Data\SqlClient\SqlStatistics.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index f0ff39a09e..f734ced9f8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -2872,11 +2872,11 @@ public IDictionary RetrieveStatistics() if (null != Statistics) { UpdateStatistics(); - return Statistics.GetHashtable(); + return Statistics.GetDictionary(); } else { - return new SqlStatistics().GetHashtable(); + return new SqlStatistics().GetDictionary(); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs deleted file mode 100644 index 7c72711cd2..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs +++ /dev/null @@ -1,221 +0,0 @@ -// 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.Collections; -using System.Diagnostics; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - internal sealed class SqlStatistics - { - - static internal SqlStatistics StartTimer(SqlStatistics statistics) - { - if ((null != statistics) && !statistics.RequestExecutionTimer()) - { - // we're re-entrant -- don't bother. - statistics = null; - } - return statistics; - } - - static internal void StopTimer(SqlStatistics statistics) - { - if (null != statistics) - { - statistics.ReleaseAndUpdateExecutionTimer(); - } - } - - // internal values that are not exposed through properties - internal long _closeTimestamp; - internal long _openTimestamp; - internal long _startExecutionTimestamp; - internal long _startFetchTimestamp; - internal long _startNetworkServerTimestamp; - - // internal values that are exposed through properties - internal long _buffersReceived; - internal long _buffersSent; - internal long _bytesReceived; - internal long _bytesSent; - internal long _connectionTime; - internal long _cursorOpens; - internal long _executionTime; - internal long _iduCount; - internal long _iduRows; - internal long _networkServerTime; - internal long _preparedExecs; - internal long _prepares; - internal long _selectCount; - internal long _selectRows; - internal long _serverRoundtrips; - internal long _sumResultSets; - internal long _transactions; - internal long _unpreparedExecs; - - // these flags are required if statistics is turned on/off in the middle of command execution - private bool _waitForDoneAfterRow; - private bool _waitForReply; - - - internal bool WaitForDoneAfterRow - { - get - { - return _waitForDoneAfterRow; - } - set - { - _waitForDoneAfterRow = value; - } - } - - internal bool WaitForReply - { - get - { - return _waitForReply; - } - } - - internal SqlStatistics() - { - } - - internal void ContinueOnNewConnection() - { - _startExecutionTimestamp = 0; - _startFetchTimestamp = 0; - _waitForDoneAfterRow = false; - _waitForReply = false; - } - - internal IDictionary GetHashtable() - { - Hashtable ht = new Hashtable(18); - - ht.Add("BuffersReceived", _buffersReceived); - ht.Add("BuffersSent", _buffersSent); - ht.Add("BytesReceived", _bytesReceived); - ht.Add("BytesSent", _bytesSent); - ht.Add("CursorOpens", _cursorOpens); - ht.Add("IduCount", _iduCount); - ht.Add("IduRows", _iduRows); - ht.Add("PreparedExecs", _preparedExecs); - ht.Add("Prepares", _prepares); - ht.Add("SelectCount", _selectCount); - ht.Add("SelectRows", _selectRows); - ht.Add("ServerRoundtrips", _serverRoundtrips); - ht.Add("SumResultSets", _sumResultSets); - ht.Add("Transactions", _transactions); - ht.Add("UnpreparedExecs", _unpreparedExecs); - - ht.Add("ConnectionTime", ADP.TimerToMilliseconds(_connectionTime)); - ht.Add("ExecutionTime", ADP.TimerToMilliseconds(_executionTime)); - ht.Add("NetworkServerTime", ADP.TimerToMilliseconds(_networkServerTime)); - - return ht; - } - - internal bool RequestExecutionTimer() - { - if (_startExecutionTimestamp == 0) - { - _startExecutionTimestamp = ADP.TimerCurrent(); - return true; - } - return false; - } - - internal void RequestNetworkServerTimer() - { - Debug.Assert(_startExecutionTimestamp != 0, "No network time expected outside execution period"); - if (_startNetworkServerTimestamp == 0) - { - _startNetworkServerTimestamp = ADP.TimerCurrent(); - } - _waitForReply = true; - } - - internal void ReleaseAndUpdateExecutionTimer() - { - if (_startExecutionTimestamp > 0) - { - _executionTime += (ADP.TimerCurrent() - _startExecutionTimestamp); - _startExecutionTimestamp = 0; - } - } - - internal void ReleaseAndUpdateNetworkServerTimer() - { - if (_waitForReply && _startNetworkServerTimestamp > 0) - { - _networkServerTime += (ADP.TimerCurrent() - _startNetworkServerTimestamp); - _startNetworkServerTimestamp = 0; - } - _waitForReply = false; - } - - internal void Reset() - { - _buffersReceived = 0; - _buffersSent = 0; - _bytesReceived = 0; - _bytesSent = 0; - _connectionTime = 0; - _cursorOpens = 0; - _executionTime = 0; - _iduCount = 0; - _iduRows = 0; - _networkServerTime = 0; - _preparedExecs = 0; - _prepares = 0; - _selectCount = 0; - _selectRows = 0; - _serverRoundtrips = 0; - _sumResultSets = 0; - _transactions = 0; - _unpreparedExecs = 0; - _waitForDoneAfterRow = false; - _waitForReply = false; - _startExecutionTimestamp = 0; - _startNetworkServerTimestamp = 0; - } - - internal void SafeAdd(ref long value, long summand) - { - if (long.MaxValue - value > summand) - { - value += summand; - } - else - { - value = long.MaxValue; - } - } - - internal long SafeIncrement(ref long value) - { - if (value < long.MaxValue) - value++; - return value; - } - - internal void UpdateStatistics() - { - // update connection time - if (_closeTimestamp >= _openTimestamp && long.MaxValue > _closeTimestamp - _openTimestamp) - { - _connectionTime = _closeTimestamp - _openTimestamp; - } - else - { - _connectionTime = long.MaxValue; - } - } - } -} - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 666ff47326..925c38901d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -1824,6 +1824,42 @@ internal class Strings { } } + /// + /// Looks up a localized string similar to Destination array is not long enough to copy all the items in the collection. Check array index and length.. + /// + internal static string Arg_ArrayPlusOffTooSmall { + get { + return ResourceManager.GetString("Arg_ArrayPlusOffTooSmall", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only single dimensional arrays are supported for the requested action.. + /// + internal static string Arg_RankMultiDimNotSupported { + get { + return ResourceManager.GetString("Arg_RankMultiDimNotSupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot remove the specified item because it was not found in the specified Collection.. + /// + internal static string Arg_RemoveArgNotFound { + get { + return ResourceManager.GetString("Arg_RemoveArgNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Non-negative number required.. + /// + internal static string ArgumentOutOfRange_NeedNonNegNum { + get { + return ResourceManager.GetString("ArgumentOutOfRange_NeedNonNegNum", resourceCulture); + } + } + /// /// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 6f12cd0ac6..1ffcff4d0b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4599,4 +4599,16 @@ '{0}' is not less than '{1}'; '{2}' cannot be greater than '{3}'. - + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs similarity index 91% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs index 4aeeb28322..7a5e37198b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs @@ -30,6 +30,11 @@ internal static void StopTimer(SqlStatistics statistics) } } + internal static ValueSqlStatisticsScope TimedScope(SqlStatistics statistics) + { + return new ValueSqlStatisticsScope(statistics); + } + // internal values that are not exposed through properties internal long _closeTimestamp; internal long _openTimestamp; @@ -61,30 +66,17 @@ internal static void StopTimer(SqlStatistics statistics) private bool _waitForDoneAfterRow; private bool _waitForReply; - - internal bool WaitForDoneAfterRow + internal SqlStatistics() { - get - { - return _waitForDoneAfterRow; - } - set - { - _waitForDoneAfterRow = value; - } } - internal bool WaitForReply + internal bool WaitForDoneAfterRow { - get - { - return _waitForReply; - } + get => _waitForDoneAfterRow; + set => _waitForDoneAfterRow = value; } - internal SqlStatistics() - { - } + internal bool WaitForReply => _waitForReply; internal void ContinueOnNewConnection() { @@ -203,7 +195,9 @@ internal void SafeAdd(ref long value, long summand) internal long SafeIncrement(ref long value) { if (value < long.MaxValue) + { value++; + } return value; } @@ -289,13 +283,21 @@ private void CopyValues(Array array, int arrayIndex) private void ValidateCopyToArguments(Array array, int arrayIndex) { if (array == null) + { throw new ArgumentNullException(nameof(array)); + } if (array.Rank != 1) + { throw new ArgumentException(Strings.Arg_RankMultiDimNotSupported); + } if (arrayIndex < 0) + { throw new ArgumentOutOfRangeException(nameof(arrayIndex), Strings.ArgumentOutOfRange_NeedNonNegNum); + } if (array.Length - arrayIndex < Count) + { throw new ArgumentException(Strings.Arg_ArrayPlusOffTooSmall); + } } private sealed class Collection : ICollection @@ -335,4 +337,16 @@ void ICollection.CopyTo(Array array, int arrayIndex) } } } + + // This is a ref struct to prevent it being included in async closures accidentally. + // Async functions should manage the timer directly using the Start and Stop method + // in their invoke and completion functions + internal readonly ref struct ValueSqlStatisticsScope // : IDisposable // ref structs cannot implement interfaces but the compiler will use pattern matching to allow use of using on them + { + private readonly SqlStatistics _statistics; + + public ValueSqlStatisticsScope(SqlStatistics statistics) => _statistics = SqlStatistics.StartTimer(statistics); + + public void Dispose() => SqlStatistics.StopTimer(_statistics); + } } From efc6c4642085ee6aec6c3f04c0c2d65fb0dfc7f5 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Fri, 7 May 2021 11:26:38 -0700 Subject: [PATCH 26/87] Update enclave sim to use new crypto apis (#1061) * update enclave sim to use new crypto apis * update using statement --- .../Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs | 6 +++--- .../Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs index bed6ada9e3..b7caa19911 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs @@ -84,9 +84,9 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); - ECParameters ecParams = KeyConverter.ECCPublicKeyBlobToParams(trustedModuleDHPublicKey); - ECDiffieHellman enclaveDHKey = ECDiffieHellman.Create(ecParams); - byte[] sharedSecret = clientDHKey.DeriveKeyFromHash(enclaveDHKey.PublicKey, HashAlgorithmName.SHA256); + byte[] sharedSecret; + using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); + sharedSecret = KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs index 064742be3c..7bda5564a8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs @@ -38,7 +38,7 @@ internal override SqlEnclaveAttestationParameters GetAttestationParameters(strin } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { ////for simulator: enclave does not send public key, and sends an empty attestation info //// The only non-trivial content it sends is the session setup info (DH pubkey of enclave) @@ -84,8 +84,9 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); - CngKey k = CngKey.Import(trustedModuleDHPublicKey, CngKeyBlobFormat.EccPublicBlob); - byte[] sharedSecret = clientDHKey.DeriveKeyMaterial(k); + byte[] sharedSecret; + using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); + sharedSecret = KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); } From 461af074621f69ae109329a8904d3c3ffa664b82 Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Sat, 8 May 2021 03:03:42 +0000 Subject: [PATCH 27/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.es.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.fr.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.it.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.ja.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.ko.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.pt-BR.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.ru.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.zh-Hans.resx | 12 ++++++++++++ .../netfx/src/Resources/Strings.zh-Hant.resx | 12 ++++++++++++ 10 files changed, 120 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 6b5765a690..33adbb02c2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -4599,4 +4599,16 @@ "{0}" ist nicht kleiner als {1}, "{2}" darf nicht größer sein als {3}. + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index fb5432e665..624b42b212 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -4599,4 +4599,16 @@ "{0}" no es menor que "{1}"; "{2}" no puede ser mayor que "{3}". + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index 8ab8d6ecca..03d85b5402 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -4599,4 +4599,16 @@ « {0} » n'est pas inférieur à « {1} ». « {2} » ne peut pas être supérieur à « {3} ». + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 65bf14742d..5c1707d369 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -4599,4 +4599,16 @@ '{0}' non è minore di '{1}'; '{2}' non può essere maggiore di '{3}'. + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index 42a8c9b56a..abf671915f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -4599,4 +4599,16 @@ '{0}' が '{1}' 未満ではありません。'{2}' は '{3}' よりも大きくできません。 + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 85c72f105f..a2b69ddaef 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -4599,4 +4599,16 @@ '{0}'이(가) '{1}'보다 작지 않습니다. '{2}'은(는) '{3}'보다 클 수 없습니다. + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index bd242cca25..7ae2681590 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -4599,4 +4599,16 @@ '{0}' não é menor que '{1}'; '{2}' não pode ser maior que '{3}'. + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 5151c1ec6a..7f5907cf56 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -4599,4 +4599,16 @@ "{0}" не меньше "{1}"; "{2}" не может быть больше "{3}". + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index fb77f3bf7a..69d2f747c1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -4599,4 +4599,16 @@ “{0}”不小于“{1}”;“{2}”不能大于“{3}”。 + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 0d2aae5c16..807d3b772f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -4599,4 +4599,16 @@ '{0}' 不小於 '{1}'; '{2}' 不能大於 '{3}'。 + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + \ No newline at end of file From 624a5d6b1735080012e2f5de13a506eaceba4fdc Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Sun, 9 May 2021 03:03:37 +0000 Subject: [PATCH 28/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 8 ++++---- .../netfx/src/Resources/Strings.es.resx | 8 ++++---- .../netfx/src/Resources/Strings.fr.resx | 8 ++++---- .../netfx/src/Resources/Strings.it.resx | 8 ++++---- .../netfx/src/Resources/Strings.ja.resx | 8 ++++---- .../netfx/src/Resources/Strings.ko.resx | 8 ++++---- .../netfx/src/Resources/Strings.pt-BR.resx | 8 ++++---- .../netfx/src/Resources/Strings.ru.resx | 8 ++++---- .../netfx/src/Resources/Strings.zh-Hans.resx | 8 ++++---- .../netfx/src/Resources/Strings.zh-Hant.resx | 8 ++++---- 10 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 33adbb02c2..c12e0f5f98 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -4600,15 +4600,15 @@ "{0}" ist nicht kleiner als {1}, "{2}" darf nicht größer sein als {3}. - Destination array is not long enough to copy all the items in the collection. Check array index and length. + Das Zielarray ist nicht lang genug, um alle Elemente in der Sammlung zu kopieren. Prüfen Sie den Index und die Länge des Arrays. - Only single dimensional arrays are supported for the requested action. + Nur eindimensionale Arrays werden für die angeforderte Aktion unterstützt. - Cannot remove the specified item because it was not found in the specified Collection. + Das angegebene Element kann nicht entfernt werden, da es nicht in der Sammlung gefunden wurde. - Non-negative number required. + Nicht negative Zahl erforderlich. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index 624b42b212..24e6ae4ed9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -4600,15 +4600,15 @@ "{0}" no es menor que "{1}"; "{2}" no puede ser mayor que "{3}". - Destination array is not long enough to copy all the items in the collection. Check array index and length. + La matriz de destino no es lo suficientemente larga para copiar todos los elementos de la colección. Compruebe el índice y la longitud de la matriz. - Only single dimensional arrays are supported for the requested action. + Solo se admiten matrices de una sola dimensión para la acción solicitada. - Cannot remove the specified item because it was not found in the specified Collection. + No se puede quitar el elemento especificado porque no se puede encontrar en la colección especificada. - Non-negative number required. + Número no negativo requerido. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index 03d85b5402..ab89be51e6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -4600,15 +4600,15 @@ « {0} » n'est pas inférieur à « {1} ». « {2} » ne peut pas être supérieur à « {3} ». - Destination array is not long enough to copy all the items in the collection. Check array index and length. + La taille du tableau de destination ne permet pas de copier tous les éléments de la collection. Vérifiez l'index et la longueur du tableau. - Only single dimensional arrays are supported for the requested action. + Seuls les tableaux unidimensionnels sont pris en charge pour l'action demandée. - Cannot remove the specified item because it was not found in the specified Collection. + Impossible de supprimer l'élément spécifié, car il est introuvable dans la collection. - Non-negative number required. + Nombre non négatif obligatoire. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 5c1707d369..e9ce6ae5f2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -4600,15 +4600,15 @@ '{0}' non è minore di '{1}'; '{2}' non può essere maggiore di '{3}'. - Destination array is not long enough to copy all the items in the collection. Check array index and length. + La lunghezza della matrice di destinazione non è sufficiente per copiare tutti gli elementi della raccolta. Controllare l'indice e la lunghezza della matrice. - Only single dimensional arrays are supported for the requested action. + Per l'azione richiesta sono supportate solo matrici unidimensionali. - Cannot remove the specified item because it was not found in the specified Collection. + Impossibile rimuovere l'elemento specificato, perché non si trova nella raccolta indicata. - Non-negative number required. + È obbligatorio un numero non negativo. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index abf671915f..99cf38ae8d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -4600,15 +4600,15 @@ '{0}' が '{1}' 未満ではありません。'{2}' は '{3}' よりも大きくできません。 - Destination array is not long enough to copy all the items in the collection. Check array index and length. + ターゲット配列の長さが不足しているため、コレクション内のすべての項目をコピーできません。配列のインデックスと長さをご確認ください。 - Only single dimensional arrays are supported for the requested action. + 要求されたアクションに対しては、1 次元配列のみがサポートされます。 - Cannot remove the specified item because it was not found in the specified Collection. + 指定された項目は、指定されたコレクションに見つからなかったため、削除できません。 - Non-negative number required. + 負でない数値が必要です。 \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index a2b69ddaef..5938ffb922 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -4600,15 +4600,15 @@ '{0}'이(가) '{1}'보다 작지 않습니다. '{2}'은(는) '{3}'보다 클 수 없습니다. - Destination array is not long enough to copy all the items in the collection. Check array index and length. + 대상 배열이 컬렉션의 모든 항목을 복사하기에 충분히 길지 않습니다. 배열 인덱스와 길이를 확인하세요. - Only single dimensional arrays are supported for the requested action. + 요청한 동작에 대해 1차원 배열만 지원됩니다. - Cannot remove the specified item because it was not found in the specified Collection. + 해당 항목은 지정된 컬렉션에 없으므로 제거할 수 없습니다. - Non-negative number required. + 음수가 아닌 수가 필요합니다. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index 7ae2681590..388e5b5d34 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -4600,15 +4600,15 @@ '{0}' não é menor que '{1}'; '{2}' não pode ser maior que '{3}'. - Destination array is not long enough to copy all the items in the collection. Check array index and length. + A matriz de destino não é longa o suficiente para copiar todos os itens da coleção. Verifique o índice e o tamanho da matriz. - Only single dimensional arrays are supported for the requested action. + A ação solicitada oferece suporte somente a matrizes dimensionais simples. - Cannot remove the specified item because it was not found in the specified Collection. + Não é possível remover o item especificado porque ele não foi encontrado na Coleção especificada. - Non-negative number required. + É necessário um número não negativo. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 7f5907cf56..e45e6d3ad5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -4600,15 +4600,15 @@ "{0}" не меньше "{1}"; "{2}" не может быть больше "{3}". - Destination array is not long enough to copy all the items in the collection. Check array index and length. + Длина конечного массива недостаточна для копирования всех элементов коллекции. Проверьте индекс и длину массива. - Only single dimensional arrays are supported for the requested action. + Для запрашиваемого действия поддерживаются только одномерные массивы. - Cannot remove the specified item because it was not found in the specified Collection. + Невозможно удалить указанный элемент, так как он не найден в заданной коллекции. - Non-negative number required. + Требуется неотрицательное число. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 69d2f747c1..9270e77566 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -4600,15 +4600,15 @@ “{0}”不小于“{1}”;“{2}”不能大于“{3}”。 - Destination array is not long enough to copy all the items in the collection. Check array index and length. + 目标数组长度不足,无法复制集合中的所有项。请检查数组索引和长度。 - Only single dimensional arrays are supported for the requested action. + 所请求的操作仅支持一维数组。 - Cannot remove the specified item because it was not found in the specified Collection. + 未在指定的集合中找到指定的项,因此无法移除该项。 - Non-negative number required. + 需要提供非负数。 \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 807d3b772f..21493e1e79 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -4600,15 +4600,15 @@ '{0}' 不小於 '{1}'; '{2}' 不能大於 '{3}'。 - Destination array is not long enough to copy all the items in the collection. Check array index and length. + 目的地陣列的長度不足以複製集合中的所有項目。請檢查陣列索引以及長度。 - Only single dimensional arrays are supported for the requested action. + 所要求的動作只支援一維陣列。 - Cannot remove the specified item because it was not found in the specified Collection. + 由於在指定的集合中找不到指定的項目,所以無法移除。 - Non-negative number required. + 需要非負數。 \ No newline at end of file From 4208104875fbf40c1862252e4cbf089d417ebb70 Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Mon, 10 May 2021 03:04:02 +0000 Subject: [PATCH 29/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.it.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index e9ce6ae5f2..b9ba1c3b11 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -4609,6 +4609,6 @@ Impossibile rimuovere l'elemento specificato, perché non si trova nella raccolta indicata. - È obbligatorio un numero non negativo. + Numero non negativo obbligatorio. \ No newline at end of file From adaa55c7787b7392c3cfad1a92d7222e1ee2a173 Mon Sep 17 00:00:00 2001 From: Wraith Date: Tue, 11 May 2021 18:19:53 +0100 Subject: [PATCH 30/87] Share SqlConnectionTimeoutErrorInternal (#1039) --- .../src/Microsoft.Data.SqlClient.csproj | 4 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 6 +- .../SqlConnectionTimeoutErrorInternal.cs | 229 ------------------ .../SqlConnectionTimeoutErrorInternal.cs | 46 ++-- 4 files changed, 35 insertions(+), 250 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs (93%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index d881f7b2b8..00c5c68859 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -470,7 +470,9 @@ - + + Microsoft\Data\SqlClient\SqlConnectionTimeoutErrorInternal.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 37e921f1d0..55cbf2500f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -453,7 +453,9 @@ - + + Microsoft\Data\SqlClient\SqlConnectionTimeoutErrorInternal.cs + Microsoft\Data\SqlClient\SqlCredential.cs @@ -614,4 +616,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs deleted file mode 100644 index 992ba2f45d..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs +++ /dev/null @@ -1,229 +0,0 @@ -// 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.Diagnostics; -using System.Text; - -namespace Microsoft.Data.SqlClient -{ - // VSTFDevDiv# 643319 - Improve timeout error message reported when SqlConnection.Open fails - internal enum SqlConnectionTimeoutErrorPhase - { - Undefined = 0, - PreLoginBegin, // [PRE-LOGIN PHASE] Start of the pre-login phase; Initialize global variables; - InitializeConnection, // [PRE-LOGIN PHASE] Create and initialize socket. - SendPreLoginHandshake, // [PRE-LOGIN PHASE] Make pre-login handshake request. - ConsumePreLoginHandshake, // [PRE-LOGIN PHASE] Receive pre-login handshake response and consume it; Establish an SSL channel. - LoginBegin, // [LOGIN PHASE] End of the pre-login phase; Start of the login phase; - ProcessConnectionAuth, // [LOGIN PHASE] Process SSPI or SQL Authenticate. - PostLogin, // [POST-LOGIN PHASE] End of the login phase; And post-login phase; - Complete, // Marker for the succesful completion of the connection - Count // ** This is to track the length of the enum. ** Do not add any phase after this. ** - } - - internal enum SqlConnectionInternalSourceType - { - Principle, - Failover, - RoutingDestination - } - - // DEVNOTE: Class to capture the duration spent in each SqlConnectionTimeoutErrorPhase. - internal class SqlConnectionTimeoutPhaseDuration - { - Stopwatch swDuration = new Stopwatch(); - - internal void StartCapture() - { - Debug.Assert(swDuration != null, "Time capture stopwatch cannot be null."); - swDuration.Start(); - } - - internal void StopCapture() - { - //Debug.Assert(swDuration.IsRunning == true, "The stop opertaion of the stopwatch cannot be called when it is not running."); - if (swDuration.IsRunning == true) - swDuration.Stop(); - } - - internal long GetMilliSecondDuration() - { - // DEVNOTE: In a phase fails in between a phase, the stop watch may still be running. - // Hence the check to verify if the stop watch is running hasn't been added in. - return swDuration.ElapsedMilliseconds; - } - } - - internal class SqlConnectionTimeoutErrorInternal - { - SqlConnectionTimeoutPhaseDuration[] phaseDurations = null; - SqlConnectionTimeoutPhaseDuration[] originalPhaseDurations = null; - - SqlConnectionTimeoutErrorPhase currentPhase = SqlConnectionTimeoutErrorPhase.Undefined; - SqlConnectionInternalSourceType currentSourceType = SqlConnectionInternalSourceType.Principle; - bool isFailoverScenario = false; - - internal SqlConnectionTimeoutErrorPhase CurrentPhase - { - get { return currentPhase; } - } - - public SqlConnectionTimeoutErrorInternal() - { - phaseDurations = new SqlConnectionTimeoutPhaseDuration[(int)SqlConnectionTimeoutErrorPhase.Count]; - for (int i = 0; i < phaseDurations.Length; i++) - phaseDurations[i] = null; - } - - public void SetFailoverScenario(bool useFailoverServer) - { - isFailoverScenario = useFailoverServer; - } - - public void SetInternalSourceType(SqlConnectionInternalSourceType sourceType) - { - currentSourceType = sourceType; - - if (currentSourceType == SqlConnectionInternalSourceType.RoutingDestination) - { - // When we get routed, save the current phase durations so that we can use them in the error message later - Debug.Assert(currentPhase == SqlConnectionTimeoutErrorPhase.PostLogin, "Should not be switching to the routing destination until Post Login is completed"); - originalPhaseDurations = phaseDurations; - phaseDurations = new SqlConnectionTimeoutPhaseDuration[(int)SqlConnectionTimeoutErrorPhase.Count]; - SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.PreLoginBegin); - } - } - - internal void ResetAndRestartPhase() - { - currentPhase = SqlConnectionTimeoutErrorPhase.PreLoginBegin; - for (int i = 0; i < phaseDurations.Length; i++) - phaseDurations[i] = null; - } - - internal void SetAndBeginPhase(SqlConnectionTimeoutErrorPhase timeoutErrorPhase) - { - currentPhase = timeoutErrorPhase; - if (phaseDurations[(int)timeoutErrorPhase] == null) - { - phaseDurations[(int)timeoutErrorPhase] = new SqlConnectionTimeoutPhaseDuration(); - } - phaseDurations[(int)timeoutErrorPhase].StartCapture(); - } - - internal void EndPhase(SqlConnectionTimeoutErrorPhase timeoutErrorPhase) - { - Debug.Assert(phaseDurations[(int)timeoutErrorPhase] != null, "End phase capture cannot be invoked when the phase duration object is a null."); - phaseDurations[(int)timeoutErrorPhase].StopCapture(); - } - - internal void SetAllCompleteMarker() - { - currentPhase = SqlConnectionTimeoutErrorPhase.Complete; - } - - internal string GetErrorMessage() - { - StringBuilder errorBuilder; - string durationString; - switch (currentPhase) - { - case SqlConnectionTimeoutErrorPhase.PreLoginBegin: - errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_Begin()); - durationString = SQLMessage.Duration_PreLogin_Begin( - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration()); - break; - case SqlConnectionTimeoutErrorPhase.InitializeConnection: - errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_InitializeConnection()); - durationString = SQLMessage.Duration_PreLogin_Begin( - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration()); - break; - case SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake: - errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_SendHandshake()); - durationString = SQLMessage.Duration_PreLoginHandshake( - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration()); - break; - case SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake: - errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_ConsumeHandshake()); - durationString = SQLMessage.Duration_PreLoginHandshake( - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration()); - break; - case SqlConnectionTimeoutErrorPhase.LoginBegin: - errorBuilder = new StringBuilder(SQLMessage.Timeout_Login_Begin()); - durationString = SQLMessage.Duration_Login_Begin( - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration()); - break; - case SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth: - errorBuilder = new StringBuilder(SQLMessage.Timeout_Login_ProcessConnectionAuth()); - durationString = SQLMessage.Duration_Login_ProcessConnectionAuth( - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration()); - break; - case SqlConnectionTimeoutErrorPhase.PostLogin: - errorBuilder = new StringBuilder(SQLMessage.Timeout_PostLogin()); - durationString = SQLMessage.Duration_PostLogin( - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration(), - phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration()); - break; - default: - errorBuilder = new StringBuilder(SQLMessage.Timeout()); - durationString = null; - break; - } - - // This message is to be added only when within the various stages of a connection. - // In all other cases, it will default to the original error message. - if ((currentPhase != SqlConnectionTimeoutErrorPhase.Undefined) && (currentPhase != SqlConnectionTimeoutErrorPhase.Complete)) - { - // NOTE: In case of a failover scenario, add a string that this failure occurred as part of the primary or secondary server - if (isFailoverScenario) - { - errorBuilder.Append(" "); - errorBuilder.AppendFormat((IFormatProvider)null, SQLMessage.Timeout_FailoverInfo(), currentSourceType); - } - else if (currentSourceType == SqlConnectionInternalSourceType.RoutingDestination) - { - errorBuilder.Append(" "); - errorBuilder.AppendFormat((IFormatProvider)null, SQLMessage.Timeout_RoutingDestination(), - originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + - originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), - originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + - originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(), - originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(), - originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration(), - originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration()); - } - } - - // NOTE: To display duration in each phase. - if (durationString != null) - { - errorBuilder.Append(" "); - errorBuilder.Append(durationString); - } - - return errorBuilder.ToString(); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs similarity index 93% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs index a6be1b2746..4e950a564c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs @@ -45,7 +45,9 @@ internal void StopCapture() { //Debug.Assert(swDuration.IsRunning == true, "The stop operation of the stopwatch cannot be called when it is not running."); if (_swDuration.IsRunning == true) + { _swDuration.Stop(); + } } internal long GetMilliSecondDuration() @@ -58,23 +60,21 @@ internal long GetMilliSecondDuration() internal class SqlConnectionTimeoutErrorInternal { - private SqlConnectionTimeoutPhaseDuration[] _phaseDurations = null; - private SqlConnectionTimeoutPhaseDuration[] _originalPhaseDurations = null; - - private SqlConnectionTimeoutErrorPhase _currentPhase = SqlConnectionTimeoutErrorPhase.Undefined; - private SqlConnectionInternalSourceType _currentSourceType = SqlConnectionInternalSourceType.Principle; - private bool _isFailoverScenario = false; + private SqlConnectionTimeoutPhaseDuration[] _phaseDurations; + private SqlConnectionTimeoutPhaseDuration[] _originalPhaseDurations; + private SqlConnectionTimeoutErrorPhase _currentPhase; + private SqlConnectionInternalSourceType _currentSourceType; + private bool _isFailoverScenario; - internal SqlConnectionTimeoutErrorPhase CurrentPhase - { - get { return _currentPhase; } - } + internal SqlConnectionTimeoutErrorPhase CurrentPhase => _currentPhase; public SqlConnectionTimeoutErrorInternal() { _phaseDurations = new SqlConnectionTimeoutPhaseDuration[(int)SqlConnectionTimeoutErrorPhase.Count]; for (int i = 0; i < _phaseDurations.Length; i++) + { _phaseDurations[i] = null; + } } public void SetFailoverScenario(bool useFailoverServer) @@ -100,7 +100,9 @@ internal void ResetAndRestartPhase() { _currentPhase = SqlConnectionTimeoutErrorPhase.PreLoginBegin; for (int i = 0; i < _phaseDurations.Length; i++) + { _phaseDurations[i] = null; + } } internal void SetAndBeginPhase(SqlConnectionTimeoutErrorPhase timeoutErrorPhase) @@ -133,20 +135,23 @@ internal string GetErrorMessage() case SqlConnectionTimeoutErrorPhase.PreLoginBegin: errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_Begin()); durationString = SQLMessage.Duration_PreLogin_Begin( - _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration()); + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + ); break; case SqlConnectionTimeoutErrorPhase.InitializeConnection: errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_InitializeConnection()); durationString = SQLMessage.Duration_PreLogin_Begin( _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + - _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration()); + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration() + ); break; case SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake: errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_SendHandshake()); durationString = SQLMessage.Duration_PreLoginHandshake( _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), - _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration()); + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + ); break; case SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake: errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_ConsumeHandshake()); @@ -154,7 +159,8 @@ internal string GetErrorMessage() _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + - _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration()); + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration() + ); break; case SqlConnectionTimeoutErrorPhase.LoginBegin: errorBuilder = new StringBuilder(SQLMessage.Timeout_Login_Begin()); @@ -163,7 +169,8 @@ internal string GetErrorMessage() _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(), _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(), - _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration()); + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration() + ); break; case SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth: errorBuilder = new StringBuilder(SQLMessage.Timeout_Login_ProcessConnectionAuth()); @@ -173,7 +180,8 @@ internal string GetErrorMessage() _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(), _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(), - _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration()); + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration() + ); break; case SqlConnectionTimeoutErrorPhase.PostLogin: errorBuilder = new StringBuilder(SQLMessage.Timeout_PostLogin()); @@ -184,7 +192,8 @@ internal string GetErrorMessage() _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(), _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(), _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration(), - _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration()); + _phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration() + ); break; default: errorBuilder = new StringBuilder(SQLMessage.Timeout()); @@ -212,7 +221,8 @@ internal string GetErrorMessage() _originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(), _originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(), _originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration(), - _originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration()); + _originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration() + ); } } From 96e7f21e57a2d2889499c4983711a4098633a8b9 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Tue, 11 May 2021 17:29:44 -0700 Subject: [PATCH 31/87] Update doc for SqlCommand.ColumnEncryptionSetting (#1066) * Update SqlCommand.xml * Update SqlCommand.xml --- doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index 2eafab88cb..5cbc8d9ab4 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -1129,14 +1129,11 @@ The following example demonstrates the use of the - Gets or sets the column encryption setting for this command. + Gets the column encryption setting for this command. The column encryption setting for this command. - - To be added. - From fee499fdb42c43d57f8daa0ab4e7179af76e7bb9 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 13 May 2021 16:37:38 -0700 Subject: [PATCH 32/87] Add sample back for old driver version docs (#1067) --- ...ionStringBuilder_AsynchronousProcessing.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 doc/samples/SqlConnectionStringBuilder_AsynchronousProcessing.cs diff --git a/doc/samples/SqlConnectionStringBuilder_AsynchronousProcessing.cs b/doc/samples/SqlConnectionStringBuilder_AsynchronousProcessing.cs new file mode 100644 index 0000000000..b3706e86bd --- /dev/null +++ b/doc/samples/SqlConnectionStringBuilder_AsynchronousProcessing.cs @@ -0,0 +1,88 @@ +using System; +using System.Data; +// +using Microsoft.Data.SqlClient; +using System.Threading; + +class Program +{ + static void Main() + { + // Create a SqlConnectionStringBuilder instance, + // and ensure that it is set up for asynchronous processing. + SqlConnectionStringBuilder builder = + new SqlConnectionStringBuilder(GetConnectionString()); + // Asynchronous method calls won't work unless you + // have added this option, or have added + // the clause "Asynchronous Processing=true" + // to the connection string. + builder.AsynchronousProcessing = true; + + string commandText = + "UPDATE Production.Product SET ReorderPoint = ReorderPoint + 1 " + + "WHERE ReorderPoint IS NOT Null;" + + "WAITFOR DELAY '0:0:3';" + + "UPDATE Production.Product SET ReorderPoint = ReorderPoint - 1 " + + "WHERE ReorderPoint IS NOT Null"; + RunCommandAsynchronously(commandText, builder.ConnectionString); + + Console.WriteLine("Press any key to finish."); + Console.ReadLine(); + } + + private static string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Integrated Security=SSPI;" + + "Initial Catalog=AdventureWorks"; + } + + private static void RunCommandAsynchronously(string commandText, + string connectionString) + { + // Given command text and connection string, asynchronously execute + // the specified command against the connection. For this example, + // the code displays an indicator as it's working, verifying the + // asynchronous behavior. + using (SqlConnection connection = new SqlConnection(connectionString)) + { + try + { + int count = 0; + SqlCommand command = new SqlCommand(commandText, connection); + connection.Open(); + IAsyncResult result = command.BeginExecuteNonQuery(); + while (!result.IsCompleted) + { + Console.WriteLine("Waiting {0}.", count); + // Wait for 1/10 second, so the counter + // doesn't consume all available resources + // on the main thread. + Thread.Sleep(100); + count += 1; + } + Console.WriteLine("Command complete. Affected {0} rows.", + command.EndExecuteNonQuery(result)); + + } + catch (SqlException ex) + { + Console.WriteLine( + "Error {0}: Microsoft.Data.SqlClient.SqlConnectionStringBuilder", + ex.Number, ex.Message); + } + catch (InvalidOperationException ex) + { + Console.WriteLine("Error: {0}", ex.Message); + } + catch (Exception ex) + { + // You might want to pass these errors + // back out to the caller. + Console.WriteLine("Error: {0}", ex.Message); + } + } + } +} +// From 561b53556434b2bf2cf9742529e0794582fb2871 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 17 May 2021 10:36:15 -0700 Subject: [PATCH 33/87] Add IP address preference support for TCP connection (#1015) --- .../SqlConnectionIPAddressPreference.xml | 40 ++++++ .../SqlConnectionStringBuilder.xml | 11 ++ .../netcore/ref/Microsoft.Data.SqlClient.cs | 16 +++ .../Interop/SNINativeMethodWrapper.Windows.cs | 10 +- .../Data/Common/DbConnectionStringCommon.cs | 110 +++++++++++++++ .../Microsoft/Data/SqlClient/SNI/SNIProxy.cs | 15 ++- .../Data/SqlClient/SNI/SNITcpHandle.cs | 120 +++++++++++------ .../Microsoft/Data/SqlClient/SqlConnection.cs | 8 ++ .../Data/SqlClient/SqlConnectionString.cs | 40 +++++- .../SqlClient/SqlConnectionStringBuilder.cs | 44 +++++- .../SqlClient/SqlInternalConnectionTds.cs | 13 +- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 13 ++ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 7 +- .../Data/SqlClient/TdsParserSafeHandles.cs | 7 +- .../Data/SqlClient/TdsParserStateObject.cs | 3 +- .../SqlClient/TdsParserStateObjectManaged.cs | 6 +- .../SqlClient/TdsParserStateObjectNative.cs | 7 +- .../netcore/src/Resources/Strings.Designer.cs | 6 + .../netcore/src/Resources/Strings.resx | 3 + .../netfx/ref/Microsoft.Data.SqlClient.cs | 17 +++ .../Data/Common/DbConnectionStringCommon.cs | 109 +++++++++++++++ .../Interop/SNINativeManagedWrapperX64.cs | 1 + .../Interop/SNINativeManagedWrapperX86.cs | 1 + .../Data/Interop/SNINativeMethodWrapper.cs | 14 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 8 ++ .../Data/SqlClient/SqlConnectionString.cs | 40 +++++- .../SqlClient/SqlConnectionStringBuilder.cs | 48 ++++++- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 13 ++ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 5 +- .../Data/SqlClient/TdsParserSafeHandles.cs | 7 +- .../Data/SqlClient/TdsParserStateObject.cs | 8 +- .../netfx/src/Resources/Strings.Designer.cs | 6 + .../netfx/src/Resources/Strings.resx | 3 + .../Data/SqlClient/SQLFallbackDNSCache.cs | 4 +- .../SqlConnectionStringBuilderTest.cs | 3 + .../FunctionalTests/SqlConnectionTest.cs | 53 +++++++- .../ManualTests/DataCommon/DataTestUtility.cs | 17 +++ ....Data.SqlClient.ManualTesting.Tests.csproj | 1 + .../AsyncCancelledConnectionsTest.cs | 10 +- .../SystemDataInternals/ConnectionHelper.cs | 23 ++++ .../ConfigurableIpPreferenceTest.cs | 125 ++++++++++++++++++ .../SQL/DNSCachingTest/DNSCachingTest.cs | 1 - .../config.default.json | 5 + 43 files changed, 894 insertions(+), 107 deletions(-) create mode 100644 doc/snippets/Microsoft.Data.SqlClient/SqlConnectionIPAddressPreference.xml create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConfigurableIpPreferenceTest/ConfigurableIpPreferenceTest.cs diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionIPAddressPreference.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionIPAddressPreference.xml new file mode 100644 index 0000000000..e713cb776b --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionIPAddressPreference.xml @@ -0,0 +1,40 @@ + + + + + Specifies a value for IP address preference during a TCP connection. + + + + + + + + Specifies a value for IP address preference during a TCP connection. + + + + + + + Connects using IPv4 address(es) first. If the connection fails, try IPv6 address(es), if provided. This is the default value. + 0 + + + Connect using IPv6 address(es) first. If the connection fails, try IPv4 address(es), if available. + 1 + + + Connects with IP addresses in the order the underlying platform or operating system provides them. + 2 + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml index 46e9ee6877..619ea2d690 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml @@ -346,6 +346,17 @@ False To set the value to null, use . + + Gets or sets the value of IP address preference. + Returns IP address preference. + + + + Gets or sets the enclave attestation Url to be used with enclave based Always Encrypted. The enclave attestation Url. diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index e630a03acb..17bc6c59c4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -398,6 +398,18 @@ public enum SqlConnectionAttestationProtocol HGS = 3 } #endif + /// + public enum SqlConnectionIPAddressPreference + { + /// + IPv4First = 0, // default + + /// + IPv6First = 1, + + /// + UsePlatformDefault = 2 + } /// public partial class SqlColumnEncryptionCertificateStoreProvider : Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider { @@ -883,6 +895,10 @@ public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbCo [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string EnclaveAttestationUrl { get { throw null; } set { } } #endif + /// + [System.ComponentModel.DisplayNameAttribute("IP Address Preference")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public Microsoft.Data.SqlClient.SqlConnectionIPAddressPreference IPAddressPreference { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Encrypt")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs index 8201ec41aa..20159ca382 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs @@ -165,6 +165,7 @@ private unsafe struct SNI_CLIENT_CONSUMER_INFO public TransparentNetworkResolutionMode transparentNetworkResolution; public int totalTimeout; public bool isAzureSqlServerEndpoint; + public SqlConnectionIPAddressPreference ipAddressPreference; public SNI_DNSCache_Info DNSCacheInfo; } @@ -275,6 +276,7 @@ internal struct SNI_Error [In] SNIHandle pConn, out IntPtr ppConn, [MarshalAs(UnmanagedType.Bool)] bool fSync, + SqlConnectionIPAddressPreference ipPreference, [In] ref SNI_DNSCache_Info pDNSCachedInfo); [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] @@ -341,7 +343,7 @@ internal static uint SNIInitialize() return SNIInitialize(IntPtr.Zero); } - internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SQLDNSInfo cachedDNSInfo) + internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) { // initialize consumer info for MARS Sni_Consumer_Info native_consumerInfo = new Sni_Consumer_Info(); @@ -353,10 +355,11 @@ internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHan native_cachedDNSInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; native_cachedDNSInfo.wszCachedTcpPort = cachedDNSInfo?.Port; - return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ref native_cachedDNSInfo); + return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ipPreference, ref native_cachedDNSInfo); } - internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, SQLDNSInfo cachedDNSInfo) + internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, + bool fSync, int timeout, bool fParallel, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) { fixed (byte* pin_instanceName = &instanceName[0]) { @@ -379,6 +382,7 @@ internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string cons clientConsumerInfo.totalTimeout = SniOpenTimeOut; clientConsumerInfo.isAzureSqlServerEndpoint = ADP.IsAzureSqlServerEndpoint(constring); + clientConsumerInfo.ipAddressPreference = ipPreference; clientConsumerInfo.DNSCacheInfo.wszCachedFQDN = cachedDNSInfo?.FQDN; clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv4 = cachedDNSInfo?.AddrIPv4; clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index eb23fcdfef..3c22c4ecd8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; @@ -400,6 +401,110 @@ internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(st #endregion + #region <> + /// + /// IP Address Preference. + /// + private readonly static Dictionary s_preferenceNames = new(StringComparer.InvariantCultureIgnoreCase); + + static DbConnectionStringBuilderUtil() + { + foreach (SqlConnectionIPAddressPreference item in Enum.GetValues(typeof(SqlConnectionIPAddressPreference))) + { + s_preferenceNames.Add(item.ToString(), item); + } + } + + /// + /// Convert a string value to the corresponding IPAddressPreference. + /// + /// The string representation of the enumeration name to convert. + /// When this method returns, `result` contains an object of type `SqlConnectionIPAddressPreference` whose value is represented by `value` if the operation succeeds. + /// If the parse operation fails, `result` contains the default value of the `SqlConnectionIPAddressPreference` type. + /// `true` if the value parameter was converted successfully; otherwise, `false`. + internal static bool TryConvertToIPAddressPreference(string value, out SqlConnectionIPAddressPreference result) + { + if (!s_preferenceNames.TryGetValue(value, out result)) + { + result = DbConnectionStringDefaults.IPAddressPreference; + return false; + } + return true; + } + + /// + /// Verifies if the `value` is defined in the expected Enum. + /// + internal static bool IsValidIPAddressPreference(SqlConnectionIPAddressPreference value) + => value == SqlConnectionIPAddressPreference.IPv4First + || value == SqlConnectionIPAddressPreference.IPv6First + || value == SqlConnectionIPAddressPreference.UsePlatformDefault; + + internal static string IPAddressPreferenceToString(SqlConnectionIPAddressPreference value) + => Enum.GetName(typeof(SqlConnectionIPAddressPreference), value); + + internal static SqlConnectionIPAddressPreference ConvertToIPAddressPreference(string keyword, object value) + { + if (value is null) + { + return DbConnectionStringDefaults.IPAddressPreference; // IPv4First + } + + if (value is string sValue) + { + // try again after remove leading & trailing whitespaces. + sValue = sValue.Trim(); + if (TryConvertToIPAddressPreference(sValue, out SqlConnectionIPAddressPreference result)) + { + return result; + } + + // string values must be valid + throw ADP.InvalidConnectionOptionValue(keyword); + } + else + { + // the value is not string, try other options + SqlConnectionIPAddressPreference eValue; + + if (value is SqlConnectionIPAddressPreference preference) + { + eValue = preference; + } + else if (value.GetType().IsEnum) + { + // explicitly block scenarios in which user tries to use wrong enum types, like: + // builder["SqlConnectionIPAddressPreference"] = EnvironmentVariableTarget.Process; + // workaround: explicitly cast non-SqlConnectionIPAddressPreference enums to int + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionIPAddressPreference), null); + } + else + { + try + { + // Enum.ToObject allows only integral and enum values (enums are blocked above), raising ArgumentException for the rest + eValue = (SqlConnectionIPAddressPreference)Enum.ToObject(typeof(SqlConnectionIPAddressPreference), value); + } + catch (ArgumentException e) + { + // to be consistent with the messages we send in case of wrong type usage, replace + // the error with our exception, and keep the original one as inner one for troubleshooting + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionIPAddressPreference), e); + } + } + + if (IsValidIPAddressPreference(eValue)) + { + return eValue; + } + else + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionIPAddressPreference), (int)eValue); + } + } + } + #endregion + internal static bool IsValidApplicationIntentValue(ApplicationIntent value) { Debug.Assert(Enum.GetNames(typeof(ApplicationIntent)).Length == 2, "ApplicationIntent enum has changed, update needed"); @@ -728,6 +833,7 @@ internal static partial class DbConnectionStringDefaults internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = _emptyString; internal const SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; + internal const SqlConnectionIPAddressPreference IPAddressPreference = SqlConnectionIPAddressPreference.IPv4First; } @@ -765,6 +871,7 @@ internal static partial class DbConnectionStringKeywords internal const string ColumnEncryptionSetting = "Column Encryption Setting"; internal const string EnclaveAttestationUrl = "Enclave Attestation Url"; internal const string AttestationProtocol = "Attestation Protocol"; + internal const string IPAddressPreference = "IP Address Preference"; // common keywords (OleDb, OracleClient, SqlClient) internal const string DataSource = "Data Source"; @@ -793,6 +900,9 @@ internal static class DbConnectionStringSynonyms //internal const string ApplicationName = APP; internal const string APP = "app"; + // internal const string IPAddressPreference = IPADDRESSPREFERENCE; + internal const string IPADDRESSPREFERENCE = "IPAddressPreference"; + //internal const string ApplicationIntent = APPLICATIONINTENT; internal const string APPLICATIONINTENT = "ApplicationIntent"; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs index e05b7498f8..5823e7f44c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs @@ -254,10 +254,12 @@ internal uint WritePacket(SNIHandle handle, SNIPacket packet, bool sync) /// Asynchronous connection /// Attempt parallel connects /// + /// IP address preference /// Used for DNS Cache - /// Used for DNS Cache + /// Used for DNS Cache /// SNI handle - internal SNIHandle CreateConnectionHandle(string fullServerName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool parallel, bool isIntegratedSecurity, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + internal SNIHandle CreateConnectionHandle(string fullServerName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, + bool flushCache, bool async, bool parallel, bool isIntegratedSecurity, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { instanceName = new byte[1]; @@ -284,7 +286,7 @@ internal SNIHandle CreateConnectionHandle(string fullServerName, bool ignoreSniO case DataSource.Protocol.Admin: case DataSource.Protocol.None: // default to using tcp if no protocol is provided case DataSource.Protocol.TCP: - sniHandle = CreateTcpHandle(details, timerExpire, parallel, cachedFQDN, ref pendingDNSInfo); + sniHandle = CreateTcpHandle(details, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo); break; case DataSource.Protocol.NP: sniHandle = CreateNpHandle(details, timerExpire, parallel); @@ -374,10 +376,11 @@ private static byte[][] GetSqlServerSPNs(string hostNameOrAddress, string portOr /// Data source /// Timer expiration /// Should MultiSubnetFailover be used + /// IP address preference /// Key for DNS Cache - /// Used for DNS Cache + /// Used for DNS Cache /// SNITCPHandle - private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool parallel, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { // TCP Format: // tcp:\ @@ -415,7 +418,7 @@ private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool port = isAdminConnection ? DefaultSqlServerDacPort : DefaultSqlServerPort; } - return new SNITCPHandle(hostName, port, timerExpire, parallel, cachedFQDN, ref pendingDNSInfo); + return new SNITCPHandle(hostName, port, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs index 4ca0631c51..d2a8341c0f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs @@ -116,9 +116,10 @@ public override int ProtocolVersion /// TCP port number /// Connection timer expiration /// Parallel executions + /// IP address preference /// Key for DNS Cache - /// Used for DNS Cache - public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + /// Used for DNS Cache + public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0}, Setting server name = {1}", args0: _connectionId, args1: serverName); @@ -147,8 +148,8 @@ public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0}, Connecting to serverName {1} and port {2}", args0: _connectionId, args1: serverName, args2: port); // We will always first try to connect with serverName as before and let the DNS server to resolve the serverName. - // If the DSN resolution fails, we will try with IPs in the DNS cache if existed. We try with IPv4 first and followed by IPv6 if - // IPv4 fails. The exceptions will be throw to upper level and be handled as before. + // If the DSN resolution fails, we will try with IPs in the DNS cache if existed. We try with cached IPs based on IPAddressPreference. + // The exceptions will be throw to upper level and be handled as before. try { if (parallel) @@ -157,7 +158,7 @@ public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel } else { - _socket = Connect(serverName, port, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); + _socket = Connect(serverName, port, ts, isInfiniteTimeOut, ipPreference, cachedFQDN, ref pendingDNSInfo); } } catch (Exception ex) @@ -175,15 +176,26 @@ public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel int portRetry = string.IsNullOrEmpty(cachedDNSInfo.Port) ? port : int.Parse(cachedDNSInfo.Port); SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0}, Retrying with cached DNS IP Address {1} and port {2}", args0: _connectionId, args1: cachedDNSInfo.AddrIPv4, args2: cachedDNSInfo.Port); + string firstCachedIP; + string secondCachedIP; + + if (SqlConnectionIPAddressPreference.IPv6First == ipPreference) { + firstCachedIP = cachedDNSInfo.AddrIPv6; + secondCachedIP = cachedDNSInfo.AddrIPv4; + } else { + firstCachedIP = cachedDNSInfo.AddrIPv4; + secondCachedIP = cachedDNSInfo.AddrIPv6; + } + try { if (parallel) { - _socket = TryConnectParallel(cachedDNSInfo.AddrIPv4, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); + _socket = TryConnectParallel(firstCachedIP, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); } else { - _socket = Connect(cachedDNSInfo.AddrIPv4, portRetry, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); + _socket = Connect(firstCachedIP, portRetry, ts, isInfiniteTimeOut, ipPreference, cachedFQDN, ref pendingDNSInfo); } } catch (Exception exRetry) @@ -194,11 +206,11 @@ public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0}, Retrying exception {1}", args0: _connectionId, args1: exRetry?.Message); if (parallel) { - _socket = TryConnectParallel(cachedDNSInfo.AddrIPv6, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); + _socket = TryConnectParallel(secondCachedIP, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); } else { - _socket = Connect(cachedDNSInfo.AddrIPv6, portRetry, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); + _socket = Connect(secondCachedIP, portRetry, ts, isInfiniteTimeOut, ipPreference, cachedFQDN, ref pendingDNSInfo); } } else @@ -320,42 +332,37 @@ private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool i // Connect to server with hostName and port. // The IP information will be collected temporarily as the pendingDNSInfo but is not stored in the DNS cache at this point. // Only write to the DNS cache when we receive IsSupported flag as true in the Feature Ext Ack from server. - private static Socket Connect(string serverName, int port, TimeSpan timeout, bool isInfiniteTimeout, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + private static Socket Connect(string serverName, int port, TimeSpan timeout, bool isInfiniteTimeout, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "IP preference : {0}", Enum.GetName(typeof(SqlConnectionIPAddressPreference), ipPreference)); + IPAddress[] ipAddresses = Dns.GetHostAddresses(serverName); string IPv4String = null; - string IPv6String = null; - + string IPv6String = null; + // Returning null socket is handled by the caller function. - if(ipAddresses == null || ipAddresses.Length == 0) + if (ipAddresses == null || ipAddresses.Length == 0) { return null; } Socket[] sockets = new Socket[ipAddresses.Length]; - AddressFamily[] preferedIPFamilies = new AddressFamily[] { AddressFamily.InterNetwork, AddressFamily.InterNetworkV6 }; - - CancellationTokenSource cts = null; + AddressFamily[] preferedIPFamilies = new AddressFamily[2]; - void Cancel() + if (ipPreference == SqlConnectionIPAddressPreference.IPv4First) { - for (int i = 0; i < sockets.Length; ++i) - { - try - { - if (sockets[i] != null && !sockets[i].Connected) - { - sockets[i].Dispose(); - sockets[i] = null; - } - } - catch (Exception e) - { - SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message); - } - } + preferedIPFamilies[0] = AddressFamily.InterNetwork; + preferedIPFamilies[1] = AddressFamily.InterNetworkV6; } + else if (ipPreference == SqlConnectionIPAddressPreference.IPv6First) + { + preferedIPFamilies[0] = AddressFamily.InterNetworkV6; + preferedIPFamilies[1] = AddressFamily.InterNetwork; + } + // else -> UsePlatformDefault + + CancellationTokenSource cts = null; if (!isInfiniteTimeout) { @@ -366,32 +373,39 @@ void Cancel() Socket availableSocket = null; try { - int n = 0; // Socket index - // We go through the IP list twice. // In the first traversal, we only try to connect with the preferedIPFamilies[0]. // In the second traversal, we only try to connect with the preferedIPFamilies[1]. + // For UsePlatformDefault preference, we do traversal once. for (int i = 0; i < preferedIPFamilies.Length; ++i) { - foreach (IPAddress ipAddress in ipAddresses) + for (int n = 0; n < ipAddresses.Length; n++) { + IPAddress ipAddress = ipAddresses[n]; try { - if (ipAddress != null && ipAddress.AddressFamily == preferedIPFamilies[i]) + if (ipAddress != null) { + if (ipAddress.AddressFamily != preferedIPFamilies[i] && ipPreference != SqlConnectionIPAddressPreference.UsePlatformDefault) + { + continue; + } + sockets[n] = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // enable keep-alive on socket SetKeepAliveValues(ref sockets[n]); - SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connecting to IP address {0} and port {1}", args0: ipAddress, args1: port); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connecting to IP address {0} and port {1} using {2} address family.", + args0: ipAddress, + args1: port, + args2: ipAddress.AddressFamily); sockets[n].Connect(ipAddress, port); - if (sockets[n] != null) // sockets[i] can be null if cancel callback is executed during connect() + if (sockets[n] != null) // sockets[n] can be null if cancel callback is executed during connect() { if (sockets[n].Connected) { availableSocket = sockets[n]; - if (ipAddress.AddressFamily == AddressFamily.InterNetwork) { IPv4String = ipAddress.ToString(); @@ -409,20 +423,21 @@ void Cancel() sockets[n] = null; } } - n++; } } catch (Exception e) { SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message); + SqlClientEventSource.Log.TryAdvancedTraceEvent($"{s_className}.{System.Reflection.MethodBase.GetCurrentMethod().Name}{EventType.ERR}THIS EXCEPTION IS BEING SWALLOWED: {e}"); } } - // If we have already got an valid Socket, we won't do the second traversal. - if (availableSocket != null) + // If we have already got a valid Socket, or the platform default was prefered + // we won't do the second traversal. + if (availableSocket != null || ipPreference == SqlConnectionIPAddressPreference.UsePlatformDefault) { break; - } + } } } finally @@ -437,6 +452,25 @@ void Cancel() } return availableSocket; + + void Cancel() + { + for (int i = 0; i < sockets.Length; ++i) + { + try + { + if (sockets[i] != null && !sockets[i].Connected) + { + sockets[i].Dispose(); + sockets[i] = null; + } + } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message); + } + } + } } private static Task ParallelConnectAsync(IPAddress[] serverAddresses, int port) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 7cdd3b56d5..d89f841b48 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -391,6 +391,14 @@ internal SqlConnectionAttestationProtocol AttestationProtocol } } + /// + /// Get IP address preference + /// + internal SqlConnectionIPAddressPreference iPAddressPreference + { + get => ((SqlConnectionString)ConnectionOptions).IPAddressPreference; + } + // This method will be called once connection string is set or changed. private void CacheConnectionStringProperties() { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index aa9023139c..d2d2cf7891 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -53,6 +53,7 @@ internal static partial class DEFAULT internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = _emptyString; internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; + internal static readonly SqlConnectionIPAddressPreference s_IPAddressPreference = SqlConnectionIPAddressPreference.IPv4First; } // SqlConnection ConnectionString Options @@ -69,6 +70,7 @@ internal static class KEY internal const string ColumnEncryptionSetting = "column encryption setting"; internal const string EnclaveAttestationUrl = "enclave attestation url"; internal const string AttestationProtocol = "attestation protocol"; + internal const string IPAddressPreference = "ip address preference"; internal const string Command_Timeout = "command timeout"; internal const string Connect_Timeout = "connect timeout"; @@ -106,6 +108,8 @@ internal static class KEY // Constant for the number of duplicate options in the connection string private static class SYNONYM { + // ip address preference + internal const string IPADDRESSPREFERENCE = "ipaddresspreference"; //application intent internal const string APPLICATIONINTENT = "applicationintent"; // application name @@ -160,9 +164,9 @@ private static class SYNONYM } #if NETCOREAPP - internal const int SynonymCount = 25; + internal const int SynonymCount = 26; #else - internal const int SynonymCount = 24; + internal const int SynonymCount = 25; #endif internal const int DeprecatedSynonymCount = 3; @@ -213,6 +217,7 @@ internal static class TRANSACTIONBINDING private readonly SqlConnectionColumnEncryptionSetting _columnEncryptionSetting; private readonly string _enclaveAttestationUrl; private readonly SqlConnectionAttestationProtocol _attestationProtocol; + private readonly SqlConnectionIPAddressPreference _ipAddressPreference; private readonly int _commandTimeout; private readonly int _connectTimeout; @@ -293,6 +298,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G _columnEncryptionSetting = ConvertValueToColumnEncryptionSetting(); _enclaveAttestationUrl = ConvertValueToString(KEY.EnclaveAttestationUrl, DEFAULT.EnclaveAttestationUrl); _attestationProtocol = ConvertValueToAttestationProtocol(); + _ipAddressPreference = ConvertValueToIPAddressPreference(); // Temporary string - this value is stored internally as an enum. string typeSystemVersionString = ConvertValueToString(KEY.Type_System_Version, null); @@ -559,6 +565,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS internal SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting { get { return _columnEncryptionSetting; } } internal string EnclaveAttestationUrl { get { return _enclaveAttestationUrl; } } internal SqlConnectionAttestationProtocol AttestationProtocol { get { return _attestationProtocol; } } + internal SqlConnectionIPAddressPreference IPAddressPreference => _ipAddressPreference; internal bool PersistSecurityInfo { get { return _persistSecurityInfo; } } internal bool Pooling { get { return _pooling; } } internal bool Replication { get { return _replication; } } @@ -687,6 +694,7 @@ private static bool CompareHostName(ref string host, string name, bool fixup) { KEY.Connect_Retry_Count, KEY.Connect_Retry_Count }, { KEY.Connect_Retry_Interval, KEY.Connect_Retry_Interval }, { KEY.Authentication, KEY.Authentication }, + { KEY.IPAddressPreference, KEY.IPAddressPreference }, { SYNONYM.APP, KEY.Application_Name }, { SYNONYM.APPLICATIONINTENT, KEY.ApplicationIntent }, @@ -717,7 +725,8 @@ private static bool CompareHostName(ref string host, string name, bool fixup) { SYNONYM.TRUSTSERVERCERTIFICATE, KEY.TrustServerCertificate }, { SYNONYM.UID, KEY.User_ID }, { SYNONYM.User, KEY.User_ID }, - { SYNONYM.WSID, KEY.Workstation_Id } + { SYNONYM.WSID, KEY.Workstation_Id }, + { SYNONYM.IPADDRESSPREFERENCE, KEY.IPAddressPreference } }; Debug.Assert(synonyms.Count == count, "incorrect initial ParseSynonyms size"); Interlocked.CompareExchange(ref s_sqlClientSynonyms, synonyms, null); @@ -898,5 +907,30 @@ internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() throw ADP.InvalidConnectionOptionValue(KEY.AttestationProtocol, e); } } + + /// + /// Convert the value to SqlConnectionIPAddressPreference + /// + /// + internal SqlConnectionIPAddressPreference ConvertValueToIPAddressPreference() + { + if (!TryGetParsetableValue(KEY.IPAddressPreference, out string value)) + { + return DEFAULT.s_IPAddressPreference; + } + + try + { + return DbConnectionStringBuilderUtil.ConvertToIPAddressPreference(KEY.IPAddressPreference, value); + } + catch (FormatException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.IPAddressPreference, e); + } + catch (OverflowException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.IPAddressPreference, e); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 0a7a06659a..9102b07a4a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -59,6 +59,7 @@ private enum Keywords AttestationProtocol, CommandTimeout, + IPAddressPreference, // keep the count value last KeywordsCount @@ -107,6 +108,7 @@ private enum Keywords private SqlConnectionColumnEncryptionSetting _columnEncryptionSetting = DbConnectionStringDefaults.ColumnEncryptionSetting; private string _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; private SqlConnectionAttestationProtocol _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; + private SqlConnectionIPAddressPreference _ipAddressPreference = DbConnectionStringDefaults.IPAddressPreference; private static string[] CreateValidKeywords() { @@ -149,6 +151,7 @@ private static string[] CreateValidKeywords() validKeywords[(int)Keywords.ColumnEncryptionSetting] = DbConnectionStringKeywords.ColumnEncryptionSetting; validKeywords[(int)Keywords.EnclaveAttestationUrl] = DbConnectionStringKeywords.EnclaveAttestationUrl; validKeywords[(int)Keywords.AttestationProtocol] = DbConnectionStringKeywords.AttestationProtocol; + validKeywords[(int)Keywords.IPAddressPreference] = DbConnectionStringKeywords.IPAddressPreference; return validKeywords; } @@ -193,7 +196,9 @@ private static string[] CreateValidKeywords() hash.Add(DbConnectionStringKeywords.ColumnEncryptionSetting, Keywords.ColumnEncryptionSetting); hash.Add(DbConnectionStringKeywords.EnclaveAttestationUrl, Keywords.EnclaveAttestationUrl); hash.Add(DbConnectionStringKeywords.AttestationProtocol, Keywords.AttestationProtocol); + hash.Add(DbConnectionStringKeywords.IPAddressPreference, Keywords.IPAddressPreference); + hash.Add(DbConnectionStringSynonyms.IPADDRESSPREFERENCE, Keywords.IPAddressPreference); hash.Add(DbConnectionStringSynonyms.APP, Keywords.ApplicationName); hash.Add(DbConnectionStringSynonyms.APPLICATIONINTENT, Keywords.ApplicationIntent); hash.Add(DbConnectionStringSynonyms.EXTENDEDPROPERTIES, Keywords.AttachDBFilename); @@ -326,6 +331,9 @@ public SqlConnectionStringBuilder(string connectionString) : base() case Keywords.AttestationProtocol: AttestationProtocol = ConvertToAttestationProtocol(keyword, value); break; + case Keywords.IPAddressPreference: + IPAddressPreference = ConvertToIPAddressPreference(keyword, value); + break; #if NETCOREAPP case Keywords.PoolBlockingPeriod: PoolBlockingPeriod = ConvertToPoolBlockingPeriod(keyword, value); break; #endif @@ -519,6 +527,22 @@ public SqlConnectionAttestationProtocol AttestationProtocol } } + /// + public SqlConnectionIPAddressPreference IPAddressPreference + { + get => _ipAddressPreference; + set + { + if (!DbConnectionStringBuilderUtil.IsValidIPAddressPreference(value)) + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionIPAddressPreference), (int)value); + } + + SetIPAddressPreferenceValue(value); + _ipAddressPreference = value; + } + } + /// public bool TrustServerCertificate { @@ -904,6 +928,14 @@ private static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(str return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(keyword, value); } + /// + /// Convert to SqlConnectionIPAddressPreference + /// + /// + /// + private static SqlConnectionIPAddressPreference ConvertToIPAddressPreference(string keyword, object value) + => DbConnectionStringBuilderUtil.ConvertToIPAddressPreference(keyword, value); + private object GetAt(Keywords index) { switch (index) @@ -980,6 +1012,8 @@ private object GetAt(Keywords index) return EnclaveAttestationUrl; case Keywords.AttestationProtocol: return AttestationProtocol; + case Keywords.IPAddressPreference: + return IPAddressPreference; default: Debug.Fail("unexpected keyword"); throw UnsupportedKeyword(s_validKeywords[(int)index]); @@ -1127,6 +1161,9 @@ private void Reset(Keywords index) case Keywords.AttestationProtocol: _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; break; + case Keywords.IPAddressPreference: + _ipAddressPreference = DbConnectionStringDefaults.IPAddressPreference; + break; default: Debug.Fail("unexpected keyword"); throw UnsupportedKeyword(s_validKeywords[(int)index]); @@ -1163,6 +1200,12 @@ private void SetAttestationProtocolValue(SqlConnectionAttestationProtocol value) base[DbConnectionStringKeywords.AttestationProtocol] = DbConnectionStringBuilderUtil.AttestationProtocolToString(value); } + private void SetIPAddressPreferenceValue(SqlConnectionIPAddressPreference value) + { + Debug.Assert(DbConnectionStringBuilderUtil.IsValidIPAddressPreference(value), "Invalid value for SqlConnectionIPAddressPreference"); + base[DbConnectionStringKeywords.IPAddressPreference] = DbConnectionStringBuilderUtil.IPAddressPreferenceToString(value); + } + private void SetAuthenticationValue(SqlAuthenticationMethod value) { Debug.Assert(DbConnectionStringBuilderUtil.IsValidAuthenticationTypeValue(value), "Invalid value for AuthenticationType"); @@ -1306,4 +1349,3 @@ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContex } } } - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 5dcda47e0f..3935ed01e4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -21,7 +21,7 @@ namespace Microsoft.Data.SqlClient { - internal class SessionStateRecord + internal sealed class SessionStateRecord { internal bool _recoverable; internal uint _version; @@ -29,7 +29,7 @@ internal class SessionStateRecord internal byte[] _data; } - internal class SessionData + internal sealed class SessionData { internal const int _maxNumberOfSessionStates = 256; internal uint _tdsVersion; @@ -101,7 +101,7 @@ public void AssertUnrecoverableStateCountIsCorrect() } } - sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposable + internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposable { // CONNECTION AND STATE VARIABLES private readonly SqlConnectionPoolGroupProviderInfo _poolGroupProviderInfo; // will only be null when called for ChangePassword, or creating SSE User Instance @@ -2481,12 +2481,9 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) internal void OnFeatureExtAck(int featureId, byte[] data) { - if (RoutingInfo != null) + if (RoutingInfo != null && TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) { - if (TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) - { - return; - } + return; } switch (featureId) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 416ec86fd0..9099f5882c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -1077,6 +1077,19 @@ public enum SqlConnectionAttestationProtocol HGS = 3 } + /// + public enum SqlConnectionIPAddressPreference + { + /// + IPv4First = 0, // default + + /// + IPv6First = 1, + + /// + UsePlatformDefault = 2 + } + /// public enum SqlConnectionColumnEncryptionSetting { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index e0ebfdf669..9e26aa375f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -412,8 +412,8 @@ internal void ProcessPendingAck(TdsParserStateObject stateObj) _connHandler.pendingSQLDNSObject = null; // AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, - out instanceName, ref _sniSpnBuffer, false, true, fParallel, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated); + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, false, true, fParallel, + _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -477,7 +477,8 @@ internal void ProcessPendingAck(TdsParserStateObject stateObj) // On Instance failure re-connect and flush SNI named instance cache. _physicalStateObj.SniContext = SniContext.Snix_Connect; - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, true, true, fParallel, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity); + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, true, true, fParallel, + _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs index 921d72a385..62411969ff 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs @@ -144,6 +144,7 @@ internal sealed class SNIHandle : SafeHandle bool flushCache, bool fSync, bool fParallel, + SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) { @@ -159,18 +160,18 @@ internal sealed class SNIHandle : SafeHandle } _status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle, - spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, cachedDNSInfo); + spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, ipPreference, cachedDNSInfo); } } // constructs SNI Handle for MARS session - internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent, SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) + internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) { try { } finally { - _status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync, cachedDNSInfo); + _status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync, ipPreference, cachedDNSInfo); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index f3299816ed..05bba67a5a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -792,7 +792,8 @@ private void ResetCancelAndProcessAttention() } } - internal abstract void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool fParallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity = false); + internal abstract void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool fParallel, + SqlConnectionIPAddressPreference iPAddressPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity = false); internal abstract void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs index 24b0c960d8..6bf08b0336 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs @@ -50,9 +50,11 @@ internal SNIMarsHandle CreateMarsSession(object callbackObject, bool async) protected override uint SNIPacketGetData(PacketHandle packet, byte[] _inBuff, ref uint dataSize) => SNIProxy.GetInstance().PacketGetData(packet.ManagedPacket, _inBuff, ref dataSize); - internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity) + internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool parallel, + SqlConnectionIPAddressPreference iPAddressPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity) { - _sessionHandle = SNIProxy.GetInstance().CreateConnectionHandle(serverName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref spnBuffer, flushCache, async, parallel, isIntegratedSecurity, cachedFQDN, ref pendingDNSInfo); + _sessionHandle = SNIProxy.GetInstance().CreateConnectionHandle(serverName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref spnBuffer, flushCache, async, parallel, isIntegratedSecurity, + iPAddressPreference, cachedFQDN, ref pendingDNSInfo); if (_sessionHandle == null) { _parser.ProcessSNIError(this); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 34c7910dde..ecb6e0bb43 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -66,7 +66,7 @@ protected override void CreateSessionHandle(TdsParserStateObject physicalConnect SQLDNSInfo cachedDNSInfo; bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); - _sessionHandle = new SNIHandle(myInfo, nativeSNIObject.Handle, cachedDNSInfo); + _sessionHandle = new SNIHandle(myInfo, nativeSNIObject.Handle, _parser.Connection.ConnectionOptions.IPAddressPreference, cachedDNSInfo); } internal override void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo) @@ -137,7 +137,8 @@ private SNINativeMethodWrapper.ConsumerInfo CreateConsumerInfo(bool async) return myInfo; } - internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool fParallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity) + internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool fParallel, + SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity) { // We assume that the loadSSPILibrary has been called already. now allocate proper length of buffer spnBuffer = new byte[1][]; @@ -171,7 +172,7 @@ internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSni SQLDNSInfo cachedDNSInfo; bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); - _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer[0], ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, cachedDNSInfo); + _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer[0], ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, ipPreference, cachedDNSInfo); } protected override uint SNIPacketGetData(PacketHandle packet, byte[] _inBuff, ref uint dataSize) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index 8a95a52351..9318d7eb40 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -4442,6 +4442,12 @@ internal class Strings { return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); } } + + /// + /// Looks up a localized string similar to Specifies an IP address preference when connecting to SQL instances. + /// + internal static string TCE_DbConnectionString_IPAddressPreference + => ResourceManager.GetString("TCE_DbConnectionString_IPAddressPreference", resourceCulture); /// /// Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted column encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'.. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index 5dd36f38ac..335803c097 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1851,6 +1851,9 @@ Specifies an attestation protocol for its corresponding enclave attestation service. + + Specifies an IP address preference when connecting to SQL instances. + The enclave type '{0}' returned from the server is not supported. diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 23c88ff5b5..7e35b64c04 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -888,6 +888,19 @@ public enum SqlConnectionAttestationProtocol HGS = 3 } + /// + public enum SqlConnectionIPAddressPreference + { + /// + IPv4First = 0, // default + + /// + IPv6First = 1, + + /// + UsePlatformDefault = 2 + } + /// public enum SqlConnectionOverrides { @@ -974,6 +987,10 @@ public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbCo [System.ComponentModel.DisplayNameAttribute("Attestation Protocol")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public Microsoft.Data.SqlClient.SqlConnectionAttestationProtocol AttestationProtocol { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("IP Address Preference")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public Microsoft.Data.SqlClient.SqlConnectionIPAddressPreference IPAddressPreference { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Encrypt")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 71ab0deea3..ec0bd3a558 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -998,6 +998,110 @@ internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(st #endregion + #region <> + /// + /// IP Address Preference. + /// + private readonly static Dictionary s_preferenceNames = new(StringComparer.InvariantCultureIgnoreCase); + + static DbConnectionStringBuilderUtil() + { + foreach (SqlConnectionIPAddressPreference item in Enum.GetValues(typeof(SqlConnectionIPAddressPreference))) + { + s_preferenceNames.Add(item.ToString(), item); + } + } + + /// + /// Convert a string value to the corresponding IPAddressPreference. + /// + /// The string representation of the enumeration name to convert. + /// When this method returns, `result` contains an object of type `SqlConnectionIPAddressPreference` whose value is represented by `value` if the operation succeeds. + /// If the parse operation fails, `result` contains the default value of the `SqlConnectionIPAddressPreference` type. + /// `true` if the value parameter was converted successfully; otherwise, `false`. + internal static bool TryConvertToIPAddressPreference(string value, out SqlConnectionIPAddressPreference result) + { + if (!s_preferenceNames.TryGetValue(value, out result)) + { + result = DbConnectionStringDefaults.IPAddressPreference; + return false; + } + return true; + } + + /// + /// Verifies if the `value` is defined in the expected Enum. + /// + internal static bool IsValidIPAddressPreference(SqlConnectionIPAddressPreference value) + => value == SqlConnectionIPAddressPreference.IPv4First + || value == SqlConnectionIPAddressPreference.IPv6First + || value == SqlConnectionIPAddressPreference.UsePlatformDefault; + + internal static string IPAddressPreferenceToString(SqlConnectionIPAddressPreference value) + => Enum.GetName(typeof(SqlConnectionIPAddressPreference), value); + + internal static SqlConnectionIPAddressPreference ConvertToIPAddressPreference(string keyword, object value) + { + if (value is null) + { + return DbConnectionStringDefaults.IPAddressPreference; // IPv4First + } + + if (value is string sValue) + { + // try again after remove leading & trailing whitespaces. + sValue = sValue.Trim(); + if (TryConvertToIPAddressPreference(sValue, out SqlConnectionIPAddressPreference result)) + { + return result; + } + + // string values must be valid + throw ADP.InvalidConnectionOptionValue(keyword); + } + else + { + // the value is not string, try other options + SqlConnectionIPAddressPreference eValue; + + if (value is SqlConnectionIPAddressPreference preference) + { + eValue = preference; + } + else if (value.GetType().IsEnum) + { + // explicitly block scenarios in which user tries to use wrong enum types, like: + // builder["SqlConnectionIPAddressPreference"] = EnvironmentVariableTarget.Process; + // workaround: explicitly cast non-SqlConnectionIPAddressPreference enums to int + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionIPAddressPreference), null); + } + else + { + try + { + // Enum.ToObject allows only integral and enum values (enums are blocked above), raising ArgumentException for the rest + eValue = (SqlConnectionIPAddressPreference)Enum.ToObject(typeof(SqlConnectionIPAddressPreference), value); + } + catch (ArgumentException e) + { + // to be consistent with the messages we send in case of wrong type usage, replace + // the error with our exception, and keep the original one as inner one for troubleshooting + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionIPAddressPreference), e); + } + } + + if (IsValidIPAddressPreference(eValue)) + { + return eValue; + } + else + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionIPAddressPreference), (int)eValue); + } + } + } + #endregion + internal static bool IsValidCertificateValue(string value) { return string.IsNullOrEmpty(value) @@ -1065,6 +1169,7 @@ internal static class DbConnectionStringDefaults internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = _emptyString; internal const SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; + internal const SqlConnectionIPAddressPreference IPAddressPreference = SqlConnectionIPAddressPreference.IPv4First; internal const string Certificate = _emptyString; internal const PoolBlockingPeriod PoolBlockingPeriod = SqlClient.PoolBlockingPeriod.Auto; } @@ -1139,6 +1244,7 @@ internal static class DbConnectionStringKeywords internal const string ColumnEncryptionSetting = "Column Encryption Setting"; internal const string EnclaveAttestationUrl = "Enclave Attestation Url"; internal const string AttestationProtocol = "Attestation Protocol"; + internal const string IPAddressPreference = "IP Address Preference"; internal const string PoolBlockingPeriod = "Pool Blocking Period"; // common keywords (OleDb, OracleClient, SqlClient) @@ -1164,6 +1270,9 @@ internal static class DbConnectionStringSynonyms //internal const string ApplicationName = APP; internal const string APP = "app"; + // internal const string IPAddressPreference = IPADDRESSPREFERENCE; + internal const string IPADDRESSPREFERENCE = "ipaddresspreference"; + //internal const string ApplicationIntent = APPLICATIONINTENT; internal const string APPLICATIONINTENT = "applicationintent"; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs index 0cddc32dc1..b28c736977 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs @@ -101,6 +101,7 @@ internal static class SNINativeManagedWrapperX64 [In] SNIHandle pConn, out IntPtr ppConn, [MarshalAs(UnmanagedType.Bool)] bool fSync, + SqlConnectionIPAddressPreference ipPreference, [In] ref SNI_DNSCache_Info pDNSCachedInfo); [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs index 398ecc4872..2dc215ad36 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs @@ -101,6 +101,7 @@ internal static class SNINativeManagedWrapperX86 [In] SNIHandle pConn, out IntPtr ppConn, [MarshalAs(UnmanagedType.Bool)] bool fSync, + SqlConnectionIPAddressPreference ipPreference, [In] ref SNI_DNSCache_Info pDNSCachedInfo); [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs index 0ac874b8b6..19dd12587a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs @@ -354,6 +354,7 @@ internal unsafe struct SNI_CLIENT_CONSUMER_INFO public TransparentNetworkResolutionMode transparentNetworkResolution; public int totalTimeout; public bool isAzureSqlServerEndpoint; + public SqlConnectionIPAddressPreference ipAddressPreference; public SNI_DNSCache_Info DNSCacheInfo; } @@ -604,11 +605,12 @@ private static uint SNIOpenSyncExWrapper(ref SNI_CLIENT_CONSUMER_INFO pClientCon [In] SNIHandle pConn, out IntPtr ppConn, [MarshalAs(UnmanagedType.Bool)] bool fSync, + SqlConnectionIPAddressPreference ipPreference, [In] ref SNI_DNSCache_Info pDNSCachedInfo) { return s_is64bitProcess ? - SNINativeManagedWrapperX64.SNIOpenWrapper(ref pConsumerInfo, szConnect, pConn, out ppConn, fSync, ref pDNSCachedInfo) : - SNINativeManagedWrapperX86.SNIOpenWrapper(ref pConsumerInfo, szConnect, pConn, out ppConn, fSync, ref pDNSCachedInfo); + SNINativeManagedWrapperX64.SNIOpenWrapper(ref pConsumerInfo, szConnect, pConn, out ppConn, fSync, ipPreference, ref pDNSCachedInfo) : + SNINativeManagedWrapperX86.SNIOpenWrapper(ref pConsumerInfo, szConnect, pConn, out ppConn, fSync, ipPreference, ref pDNSCachedInfo); } private static IntPtr SNIPacketAllocateWrapper([In] SafeHandle pConn, IOType IOType) @@ -758,7 +760,7 @@ internal static uint SNIInitialize() return SNIInitialize(IntPtr.Zero); } - internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SQLDNSInfo cachedDNSInfo) + internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) { // initialize consumer info for MARS Sni_Consumer_Info native_consumerInfo = new Sni_Consumer_Info(); @@ -770,10 +772,11 @@ internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHan native_cachedDNSInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; native_cachedDNSInfo.wszCachedTcpPort = cachedDNSInfo?.Port; - return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ref native_cachedDNSInfo); + return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ipPreference, ref native_cachedDNSInfo); } - internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, Int32 transparentNetworkResolutionStateNo, Int32 totalTimeout, Boolean isAzureSqlServerEndpoint, SQLDNSInfo cachedDNSInfo) + internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, + Int32 transparentNetworkResolutionStateNo, Int32 totalTimeout, Boolean isAzureSqlServerEndpoint, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) { fixed (byte* pin_instanceName = &instanceName[0]) { @@ -808,6 +811,7 @@ internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string cons }; clientConsumerInfo.totalTimeout = totalTimeout; + clientConsumerInfo.ipAddressPreference = ipPreference; clientConsumerInfo.DNSCacheInfo.wszCachedFQDN = cachedDNSInfo?.FQDN; clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv4 = cachedDNSInfo?.AddrIPv4; clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index f734ced9f8..3c9a6a4a1a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -588,6 +588,14 @@ internal SqlConnectionAttestationProtocol AttestationProtocol } } + /// + /// Get IP address preference + /// + internal SqlConnectionIPAddressPreference iPAddressPreference + { + get => ((SqlConnectionString)ConnectionOptions).IPAddressPreference; + } + // Is this connection is a Context Connection? private bool UsesContextConnection(SqlConnectionString opt) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 2b3ffa9d70..761ab74751 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -58,6 +58,7 @@ internal static class DEFAULT internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = _emptyString; internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; + internal static readonly SqlConnectionIPAddressPreference s_IPAddressPreference = SqlConnectionIPAddressPreference.IPv4First; #if ADONET_CERT_AUTH internal const string Certificate = _emptyString; @@ -76,6 +77,7 @@ internal static class KEY internal const string ColumnEncryptionSetting = "column encryption setting"; internal const string EnclaveAttestationUrl = "enclave attestation url"; internal const string AttestationProtocol = "attestation protocol"; + internal const string IPAddressPreference = "ip address preference"; internal const string Connect_Timeout = "connect timeout"; internal const string Command_Timeout = "command timeout"; internal const string Connection_Reset = "connection reset"; @@ -118,6 +120,8 @@ internal static class KEY private static class SYNONYM { + // ip address preference + internal const string IPADDRESSPREFERENCE = "ipaddresspreference"; // application intent internal const string APPLICATIONINTENT = "applicationintent"; // application name @@ -172,7 +176,7 @@ private static class SYNONYM // make sure to update SynonymCount value below when adding or removing synonyms } - internal const int SynonymCount = 29; + internal const int SynonymCount = 30; // the following are all inserted as keys into the _netlibMapping hash internal static class NETLIB @@ -239,6 +243,7 @@ internal static class TRANSACIONBINDING private readonly SqlConnectionColumnEncryptionSetting _columnEncryptionSetting; private readonly string _enclaveAttestationUrl; private readonly SqlConnectionAttestationProtocol _attestationProtocol; + private readonly SqlConnectionIPAddressPreference _ipAddressPreference; private readonly int _commandTimeout; private readonly int _connectTimeout; @@ -325,6 +330,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G _columnEncryptionSetting = ConvertValueToColumnEncryptionSetting(); _enclaveAttestationUrl = ConvertValueToString(KEY.EnclaveAttestationUrl, DEFAULT.EnclaveAttestationUrl); _attestationProtocol = ConvertValueToAttestationProtocol(); + _ipAddressPreference = ConvertValueToIPAddressPreference(); #if ADONET_CERT_AUTH _certificate = ConvertValueToString(KEY.Certificate, DEFAULT.Certificate); @@ -682,6 +688,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS internal SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting { get { return _columnEncryptionSetting; } } internal string EnclaveAttestationUrl { get { return _enclaveAttestationUrl; } } internal SqlConnectionAttestationProtocol AttestationProtocol { get { return _attestationProtocol; } } + internal SqlConnectionIPAddressPreference IPAddressPreference => _ipAddressPreference; #if ADONET_CERT_AUTH internal string Certificate { get { return _certificate; } } internal bool UsesCertificate { get { return _authType == SqlClient.SqlAuthenticationMethod.SqlCertificate; } } @@ -822,6 +829,7 @@ internal static Hashtable GetParseSynonyms() hash.Add(KEY.Connect_Retry_Count, KEY.Connect_Retry_Count); hash.Add(KEY.Connect_Retry_Interval, KEY.Connect_Retry_Interval); hash.Add(KEY.Authentication, KEY.Authentication); + hash.Add(KEY.IPAddressPreference, KEY.IPAddressPreference); #if ADONET_CERT_AUTH hash.Add(KEY.Certificate, KEY.Certificate); #endif @@ -854,6 +862,7 @@ internal static Hashtable GetParseSynonyms() hash.Add(SYNONYM.UID, KEY.User_ID); hash.Add(SYNONYM.User, KEY.User_ID); hash.Add(SYNONYM.WSID, KEY.Workstation_Id); + hash.Add(SYNONYM.IPADDRESSPREFERENCE, KEY.IPAddressPreference); Debug.Assert(SqlConnectionStringBuilder.KeywordsCount + SynonymCount == hash.Count, "incorrect initial ParseSynonyms size"); _sqlClientSynonyms = hash; } @@ -1077,6 +1086,34 @@ internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() } } + /// + /// Convert the value to SqlConnectionIPAddressPreference + /// + /// + internal SqlConnectionIPAddressPreference ConvertValueToIPAddressPreference() + { + object value = base.Parsetable[KEY.IPAddressPreference]; + + string valStr = value as string; + if (valStr == null) + { + return DEFAULT.s_IPAddressPreference; + } + + try + { + return DbConnectionStringBuilderUtil.ConvertToIPAddressPreference(KEY.IPAddressPreference, valStr); + } + catch (FormatException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.IPAddressPreference, e); + } + catch (OverflowException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.IPAddressPreference, e); + } + } + internal bool ConvertValueToEncrypt() { // If the Authentication keyword is provided, default to Encrypt=true; @@ -1087,4 +1124,3 @@ internal bool ConvertValueToEncrypt() } } } - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 17d62e41bd..15590fe981 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -65,6 +65,7 @@ private enum Keywords AttestationProtocol, CommandTimeout, + IPAddressPreference, #if ADONET_CERT_AUTH Certificate, @@ -118,6 +119,7 @@ private enum Keywords private SqlConnectionColumnEncryptionSetting _columnEncryptionSetting = DbConnectionStringDefaults.ColumnEncryptionSetting; private string _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; private SqlConnectionAttestationProtocol _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; + private SqlConnectionIPAddressPreference _ipAddressPreference = DbConnectionStringDefaults.IPAddressPreference; private PoolBlockingPeriod _poolBlockingPeriod = DbConnectionStringDefaults.PoolBlockingPeriod; #if ADONET_CERT_AUTH @@ -168,6 +170,7 @@ static SqlConnectionStringBuilder() validKeywords[(int)Keywords.ColumnEncryptionSetting] = DbConnectionStringKeywords.ColumnEncryptionSetting; validKeywords[(int)Keywords.EnclaveAttestationUrl] = DbConnectionStringKeywords.EnclaveAttestationUrl; validKeywords[(int)Keywords.AttestationProtocol] = DbConnectionStringKeywords.AttestationProtocol; + validKeywords[(int)Keywords.IPAddressPreference] = DbConnectionStringKeywords.IPAddressPreference; #if ADONET_CERT_AUTH validKeywords[(int)Keywords.Certificate] = DbConnectionStringKeywords.Certificate; #endif @@ -215,9 +218,11 @@ static SqlConnectionStringBuilder() hash.Add(DbConnectionStringKeywords.ColumnEncryptionSetting, Keywords.ColumnEncryptionSetting); hash.Add(DbConnectionStringKeywords.EnclaveAttestationUrl, Keywords.EnclaveAttestationUrl); hash.Add(DbConnectionStringKeywords.AttestationProtocol, Keywords.AttestationProtocol); + hash.Add(DbConnectionStringKeywords.IPAddressPreference, Keywords.IPAddressPreference); #if ADONET_CERT_AUTH hash.Add(DbConnectionStringKeywords.Certificate, Keywords.Certificate); #endif + hash.Add(DbConnectionStringSynonyms.IPADDRESSPREFERENCE, Keywords.IPAddressPreference); hash.Add(DbConnectionStringSynonyms.APP, Keywords.ApplicationName); hash.Add(DbConnectionStringSynonyms.APPLICATIONINTENT, Keywords.ApplicationIntent); hash.Add(DbConnectionStringSynonyms.Async, Keywords.AsynchronousProcessing); @@ -357,6 +362,9 @@ public SqlConnectionStringBuilder(string connectionString) : base() case Keywords.AttestationProtocol: AttestationProtocol = ConvertToAttestationProtocol(keyword, value); break; + case Keywords.IPAddressPreference: + IPAddressPreference = ConvertToIPAddressPreference(keyword, value); + break; #if ADONET_CERT_AUTH case Keywords.Certificate: Certificate = ConvertToString(value); @@ -688,6 +696,26 @@ public SqlConnectionAttestationProtocol AttestationProtocol } } + /// + [DisplayName(DbConnectionStringKeywords.IPAddressPreference)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] + [ResDescriptionAttribute(StringsHelper.ResourceNames.TCE_DbConnectionString_IPAddressPreference)] + [RefreshPropertiesAttribute(RefreshProperties.All)] + public SqlConnectionIPAddressPreference IPAddressPreference + { + get => _ipAddressPreference; + set + { + if (!DbConnectionStringBuilderUtil.IsValidIPAddressPreference(value)) + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionIPAddressPreference), (int)value); + } + + SetIPAddressPreferenceValue(value); + _ipAddressPreference = value; + } + } + /// [DisplayName(DbConnectionStringKeywords.TrustServerCertificate)] [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] @@ -1267,6 +1295,14 @@ private static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(str return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(keyword, value); } + /// + /// Convert to SqlConnectionIPAddressPreference + /// + /// + /// + private static SqlConnectionIPAddressPreference ConvertToIPAddressPreference(string keyword, object value) + => DbConnectionStringBuilderUtil.ConvertToIPAddressPreference(keyword, value); + private object GetAt(Keywords index) { switch (index) @@ -1357,6 +1393,8 @@ private object GetAt(Keywords index) return EnclaveAttestationUrl; case Keywords.AttestationProtocol: return AttestationProtocol; + case Keywords.IPAddressPreference: + return IPAddressPreference; #if ADONET_CERT_AUTH case Keywords.Certificate: return Certificate; #endif @@ -1552,6 +1590,9 @@ private void Reset(Keywords index) case Keywords.AttestationProtocol: _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; break; + case Keywords.IPAddressPreference: + _ipAddressPreference = DbConnectionStringDefaults.IPAddressPreference; + break; default: Debug.Fail("unexpected keyword"); throw ADP.KeywordNotSupported(_validKeywords[(int)index]); @@ -1598,6 +1639,12 @@ private void SetAttestationProtocolValue(SqlConnectionAttestationProtocol value) base[DbConnectionStringKeywords.AttestationProtocol] = DbConnectionStringBuilderUtil.AttestationProtocolToString(value); } + private void SetIPAddressPreferenceValue(SqlConnectionIPAddressPreference value) + { + Debug.Assert(DbConnectionStringBuilderUtil.IsValidIPAddressPreference(value), "Invalid value for SqlConnectionIPAddressPreference"); + base[DbConnectionStringKeywords.IPAddressPreference] = DbConnectionStringBuilderUtil.IPAddressPreferenceToString(value); + } + /// public override bool ShouldSerialize(string keyword) @@ -1923,4 +1970,3 @@ private System.ComponentModel.Design.Serialization.InstanceDescriptor ConvertToI } } - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 5e422fef74..4ac0a23fa4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -1076,6 +1076,19 @@ public enum SqlConnectionAttestationProtocol HGS = 3 } + /// + public enum SqlConnectionIPAddressPreference + { + /// + IPv4First = 0, // default + + /// + IPv6First = 1, + + /// + UsePlatformDefault = 2 + } + /// public enum SqlAuthenticationMethod { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 6b4d322c1f..78015db428 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -593,7 +593,7 @@ internal void ProcessPendingAck(TdsParserStateObject stateObj) } _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, - out instanceName, _sniSpnBuffer, false, true, fParallel, transparentNetworkResolutionState, totalTimeout, FQDNforDNSCahce); + out instanceName, _sniSpnBuffer, false, true, fParallel, transparentNetworkResolutionState, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCahce); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -656,7 +656,8 @@ internal void ProcessPendingAck(TdsParserStateObject stateObj) // On Instance failure re-connect and flush SNI named instance cache. _physicalStateObj.SniContext = SniContext.Snix_Connect; - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, _sniSpnBuffer, true, true, fParallel, transparentNetworkResolutionState, totalTimeout, serverInfo.ResolvedServerName); + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, + out instanceName, _sniSpnBuffer, true, true, fParallel, transparentNetworkResolutionState, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, serverInfo.ResolvedServerName); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs index 30e874995c..b61ed1dd34 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs @@ -150,6 +150,7 @@ internal sealed class SNIHandle : SafeHandle bool fParallel, TransparentNetworkResolutionState transparentNetworkResolutionState, int totalTimeout, + SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) { @@ -172,19 +173,19 @@ internal sealed class SNIHandle : SafeHandle int transparentNetworkResolutionStateNo = (int)transparentNetworkResolutionState; _status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle, spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, transparentNetworkResolutionStateNo, totalTimeout, - ADP.IsAzureSqlServerEndpoint(serverName), cachedDNSInfo); + ADP.IsAzureSqlServerEndpoint(serverName), ipPreference, cachedDNSInfo); } } // constructs SNI Handle for MARS session - internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent, SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) + internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) { RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { - _status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync, cachedDNSInfo); + _status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync, ipPreference, cachedDNSInfo); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index ebd75aaefa..9453f6102a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -326,7 +326,7 @@ internal TdsParserStateObject(TdsParser parser, SNIHandle physicalConnection, bo SQLDNSInfo cachedDNSInfo; bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); - _sessionHandle = new SNIHandle(myInfo, physicalConnection, cachedDNSInfo); + _sessionHandle = new SNIHandle(myInfo, physicalConnection, _parser.Connection.ConnectionOptions.IPAddressPreference, cachedDNSInfo); if (_sessionHandle.Status != TdsEnums.SNI_SUCCESS) { AddError(parser.ProcessSNIError(this)); @@ -852,7 +852,8 @@ private SNINativeMethodWrapper.ConsumerInfo CreateConsumerInfo(bool async) return myInfo; } - internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, byte[] spnBuffer, bool flushCache, bool async, bool fParallel, TransparentNetworkResolutionState transparentNetworkResolutionState, int totalTimeout, string cachedFQDN) + internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, byte[] spnBuffer, bool flushCache, + bool async, bool fParallel, TransparentNetworkResolutionState transparentNetworkResolutionState, int totalTimeout, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN) { SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); @@ -880,7 +881,8 @@ internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeo SQLDNSInfo cachedDNSInfo; bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); - _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, cachedDNSInfo); + _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), + out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, ipPreference, cachedDNSInfo); } internal bool Deactivate() diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 925c38901d..f5b5f44a4c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -12077,6 +12077,12 @@ internal class Strings { return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); } } + + /// + /// Looks up a localized string similar to Specifies an IP address preference when connecting to SQL instances. + /// + internal static string TCE_DbConnectionString_IPAddressPreference + => ResourceManager.GetString("TCE_DbConnectionString_IPAddressPreference", resourceCulture); /// /// Looks up a localized string similar to Default column encryption setting for all the commands on the connection.. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 1ffcff4d0b..7ca22b2fe8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4524,6 +4524,9 @@ Specifies an attestation protocol for its corresponding enclave attestation service. + + Specifies an IP address preference when connecting to SQL instances. + The enclave type '{0}' returned from the server is not supported. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs index e18b61cee4..9d4136d01f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs @@ -7,7 +7,7 @@ namespace Microsoft.Data.SqlClient { - internal class SQLFallbackDNSCache + internal sealed class SQLFallbackDNSCache { private static readonly SQLFallbackDNSCache _SQLFallbackDNSCache = new SQLFallbackDNSCache(); private static readonly int initialCapacity = 101; // give some prime number here according to MSDN docs. It will be resized if reached capacity. @@ -68,7 +68,7 @@ internal bool IsDuplicate(SQLDNSInfo newItem) } } - internal class SQLDNSInfo + internal sealed class SQLDNSInfo { public string FQDN { get; set; } public string AddrIPv4 { get; set; } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs index 9796b297c2..4a61e10edd 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs @@ -55,6 +55,9 @@ public partial class SqlConnectionStringBuilderTest [InlineData("Initial Catalog = Northwind; Failover Partner = randomserver.sys.local")] [InlineData("Initial Catalog = tempdb")] [InlineData("Integrated Security = true")] + [InlineData("IPAddressPreference = IPv4First")] + [InlineData("IPAddressPreference = IPv6First")] + [InlineData("IPAddressPreference = UsePlatformDefault")] [InlineData("Trusted_Connection = false")] [InlineData("Max Pool Size = 50")] [InlineData("Min Pool Size = 20")] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs index 9289f23768..b3afc51527 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.Tests { public partial class SqlConnectionTest { - private static readonly string[] s_retrieveInternalInfoKeys = + private static readonly string[] s_retrieveInternalInfoKeys = { "SQLDNSCachingSupportedState", "SQLDNSCachingSupportedStateBeforeRedirect" @@ -53,7 +53,7 @@ public void Constructor2() Assert.Null(cn.Site); Assert.Equal(ConnectionState.Closed, cn.State); Assert.False(cn.StatisticsEnabled); - Assert.True(string.Compare (Environment.MachineName, cn.WorkstationId, true) == 0); + Assert.True(string.Compare(Environment.MachineName, cn.WorkstationId, true) == 0); cn = new SqlConnection((string)null); Assert.Equal(string.Empty, cn.ConnectionString); @@ -67,7 +67,7 @@ public void Constructor2() Assert.Null(cn.Site); Assert.Equal(ConnectionState.Closed, cn.State); Assert.False(cn.StatisticsEnabled); - Assert.True(string.Compare (Environment.MachineName, cn.WorkstationId, true) == 0); + Assert.True(string.Compare(Environment.MachineName, cn.WorkstationId, true) == 0); } [Fact] @@ -107,7 +107,7 @@ public void Constructor2_ConnectionString_Invalid() try { new SqlConnection("Packet Size=511"); - } + } catch (ArgumentException ex) { // Invalid 'Packet Size'. The value must be an @@ -1326,7 +1326,7 @@ public void RetrieveInternalInfo_ExpectedKeysInDictionary_Success() Assert.NotEmpty(d.Values); Assert.Equal(s_retrieveInternalInfoKeys.Length, d.Values.Count); - foreach(string key in s_retrieveInternalInfoKeys) + foreach (string key in s_retrieveInternalInfoKeys) { Assert.True(d.ContainsKey(key)); @@ -1343,5 +1343,48 @@ public void RetrieveInternalInfo_UnexpectedKeysInDictionary_Success() IDictionary d = cn.RetrieveInternalInfo(); Assert.False(d.ContainsKey("Foo")); } + + [Fact] + public void ConnectionString_IPAddressPreference() + { + SqlConnection cn = new SqlConnection(); + cn.ConnectionString = "IPAddressPreference=IPv4First"; + cn.ConnectionString = "IPAddressPreference=IPV4FIRST"; + cn.ConnectionString = "IPAddressPreference=ipv4first"; + cn.ConnectionString = "IPAddressPreference=iPv4FirSt"; + cn.ConnectionString = "IPAddressPreference=IPv6First"; + cn.ConnectionString = "IPAddressPreference=IPV6FIRST"; + cn.ConnectionString = "IPAddressPreference=ipv6first"; + cn.ConnectionString = "IPAddressPreference=iPv6FirST"; + cn.ConnectionString = "IPAddressPreference=UsePlatformDefault"; + cn.ConnectionString = "IPAddressPreference=USEPLATFORMDEFAULT"; + cn.ConnectionString = "IPAddressPreference=useplatformdefault"; + cn.ConnectionString = "IPAddressPreference=usePlAtFormdeFault"; + } + + [Theory] + [InlineData("IPAddressPreference=-1")] + [InlineData("IPAddressPreference=0")] + [InlineData("IPAddressPreference=!@#")] + [InlineData("IPAddressPreference=ABC")] + [InlineData("IPAddressPreference=ipv6")] + public void ConnectionString_IPAddressPreference_Invalid(string value) + { + SqlConnection cn = new SqlConnection(); + try + { + cn.ConnectionString = value; + Assert.True(false, $"It mustn't come to this line; Value '{value}' should be invalid."); + } + catch (ArgumentException ex) + { + // Invalid value for key 'ip address preference' + Assert.Equal(typeof(ArgumentException), ex.GetType()); + Assert.Null(ex.InnerException); + Assert.NotNull(ex.Message); + Assert.Contains("'ip address preference'", ex.Message); + Assert.Null(ex.ParamName); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index a97a16a05d..2aefb3fe36 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -9,6 +9,9 @@ using System.Diagnostics.Tracing; using System.Globalization; using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; using System.Security; using System.Threading; using System.Threading.Tasks; @@ -343,6 +346,20 @@ public static bool IsTCPConnectionStringPasswordIncluded() return RetrieveValueFromConnStr(TCPConnectionString, new string[] { "Password", "PWD" }) != string.Empty; } + public static bool DoesHostAddressContainBothIPv4AndIPv6() + { + if (!IsDNSCachingSetup()) + { + return false; + } + using (var connection = new SqlConnection(DNSCachingConnString)) + { + List ipAddresses = Dns.GetHostAddresses(connection.DataSource).ToList(); + return ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetwork) && + ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetworkV6); + } + } + /// /// Generate a unique name to use in Sql Server; /// some providers does not support names (Oracle supports up to 30). 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 0878e72f74..0a1946d7cc 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 @@ -70,6 +70,7 @@ Common\System\Collections\DictionaryExtensions.cs + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs index 2e2a1ca022..e71d6d62f6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs @@ -28,18 +28,14 @@ public void CancelAsyncConnections() { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); builder.MultipleActiveResultSets = false; - RunCancelAsyncConnections(builder, false); - RunCancelAsyncConnections(builder, true); + RunCancelAsyncConnections(builder); builder.MultipleActiveResultSets = true; - RunCancelAsyncConnections(builder, false); - RunCancelAsyncConnections(builder, true); + RunCancelAsyncConnections(builder); } - private void RunCancelAsyncConnections(SqlConnectionStringBuilder connectionStringBuilder, bool makeAsyncBlocking) + private void RunCancelAsyncConnections(SqlConnectionStringBuilder connectionStringBuilder) { SqlConnection.ClearAllPools(); - AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking", makeAsyncBlocking); - _watch = Stopwatch.StartNew(); _random = new Random(4); // chosen via fair dice role. ParallelLoopResult results = new ParallelLoopResult(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/ConnectionHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/ConnectionHelper.cs index 54561e1be9..32bac50d08 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/ConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/ConnectionHelper.cs @@ -17,6 +17,7 @@ internal static class ConnectionHelper private static Type s_dbConnectionInternal = s_MicrosoftDotData.GetType("Microsoft.Data.ProviderBase.DbConnectionInternal"); private static Type s_tdsParser = s_MicrosoftDotData.GetType("Microsoft.Data.SqlClient.TdsParser"); private static Type s_tdsParserStateObject = s_MicrosoftDotData.GetType("Microsoft.Data.SqlClient.TdsParserStateObject"); + private static Type s_SQLDNSInfo = s_MicrosoftDotData.GetType("Microsoft.Data.SqlClient.SQLDNSInfo"); private static PropertyInfo s_sqlConnectionInternalConnection = s_sqlConnection.GetProperty("InnerConnection", BindingFlags.Instance | BindingFlags.NonPublic); private static PropertyInfo s_dbConnectionInternalPool = s_dbConnectionInternal.GetProperty("Pool", BindingFlags.Instance | BindingFlags.NonPublic); private static MethodInfo s_dbConnectionInternalIsConnectionAlive = s_dbConnectionInternal.GetMethod("IsConnectionAlive", BindingFlags.Instance | BindingFlags.NonPublic); @@ -26,6 +27,11 @@ internal static class ConnectionHelper private static FieldInfo s_tdsParserStateObjectProperty = s_tdsParser.GetField("_physicalStateObj", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo s_enforceTimeoutDelayProperty = s_tdsParserStateObject.GetField("_enforceTimeoutDelay", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo s_enforcedTimeoutDelayInMilliSeconds = s_tdsParserStateObject.GetField("_enforcedTimeoutDelayInMilliSeconds", BindingFlags.Instance | BindingFlags.NonPublic); + private static FieldInfo s_pendingSQLDNSObject = s_sqlInternalConnectionTds.GetField("pendingSQLDNSObject", BindingFlags.Instance | BindingFlags.NonPublic); + private static PropertyInfo s_pendingSQLDNS_FQDN = s_SQLDNSInfo.GetProperty("FQDN", BindingFlags.Instance | BindingFlags.Public); + private static PropertyInfo s_pendingSQLDNS_AddrIPv4 = s_SQLDNSInfo.GetProperty("AddrIPv4", BindingFlags.Instance | BindingFlags.Public); + private static PropertyInfo s_pendingSQLDNS_AddrIPv6 = s_SQLDNSInfo.GetProperty("AddrIPv6", BindingFlags.Instance | BindingFlags.Public); + private static PropertyInfo s_pendingSQLDNS_Port = s_SQLDNSInfo.GetProperty("Port", BindingFlags.Instance | BindingFlags.Public); public static object GetConnectionPool(object internalConnection) { @@ -79,5 +85,22 @@ public static void SetEnforcedTimeout(this SqlConnection connection, bool enforc s_enforceTimeoutDelayProperty.SetValue(stateObj, enforce); s_enforcedTimeoutDelayInMilliSeconds.SetValue(stateObj, timeout); } + + /// + /// Resolve the established socket end point information for TCP protocol. + /// + /// Active connection to extract the requested data + /// FQDN, AddrIPv4, AddrIPv6, and Port in sequence + public static Tuple GetSQLDNSInfo(this SqlConnection connection) + { + object internalConnection = GetInternalConnection(connection); + VerifyObjectIsInternalConnection(internalConnection); + object pendingSQLDNSInfo = s_pendingSQLDNSObject.GetValue(internalConnection); + string fqdn = s_pendingSQLDNS_FQDN.GetValue(pendingSQLDNSInfo) as string; + string ipv4 = s_pendingSQLDNS_AddrIPv4.GetValue(pendingSQLDNSInfo) as string; + string ipv6 = s_pendingSQLDNS_AddrIPv6.GetValue(pendingSQLDNSInfo) as string; + string port = s_pendingSQLDNS_Port.GetValue(pendingSQLDNSInfo) as string; + return new Tuple(fqdn, ipv4, ipv6, port); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConfigurableIpPreferenceTest/ConfigurableIpPreferenceTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConfigurableIpPreferenceTest/ConfigurableIpPreferenceTest.cs new file mode 100644 index 0000000000..8003660889 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConfigurableIpPreferenceTest/ConfigurableIpPreferenceTest.cs @@ -0,0 +1,125 @@ +// 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.Generic; +using System.Data; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using Microsoft.Data.SqlClient.ManualTesting.Tests.SystemDataInternals; +using Xunit; + +using static Microsoft.Data.SqlClient.ManualTesting.Tests.DataTestUtility; +using static Microsoft.Data.SqlClient.ManualTesting.Tests.DNSCachingTest; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + public class ConfigurableIpPreferenceTest + { + private const string CnnPrefIPv6 = ";IPAddressPreference=IPv6First"; + private const string CnnPrefIPv4 = ";IPAddressPreference=IPv4First"; + + private static bool IsTCPConnectionStringSetup() => !string.IsNullOrEmpty(TCPConnectionString); + private static bool IsValidDataSource() + { + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(TCPConnectionString); + int startIdx = builder.DataSource.IndexOf(':') + 1; + int endIdx = builder.DataSource.IndexOf(','); + string serverName; + if (endIdx == -1) + { + serverName = builder.DataSource.Substring(startIdx); + } + else + { + serverName = builder.DataSource.Substring(startIdx, endIdx - startIdx); + } + + List ipAddresses = Dns.GetHostAddresses(serverName).ToList(); + return ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetwork) && + ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetworkV6); + } + + [ConditionalTheory(nameof(IsTCPConnectionStringSetup), nameof(IsValidDataSource))] + [InlineData(CnnPrefIPv6)] + [InlineData(CnnPrefIPv4)] + [InlineData(";IPAddressPreference=UsePlatformDefault")] + public void ConfigurableIpPreference(string ipPreference) + { + using (SqlConnection connection = new SqlConnection(TCPConnectionString + ipPreference +#if NETFRAMEWORK + + ";TransparentNetworkIPResolution=false" // doesn't support in .NET Core +#endif + )) + { + connection.Open(); + Assert.Equal(ConnectionState.Open, connection.State); + Tuple DNSInfo = connection.GetSQLDNSInfo(); + if(ipPreference == CnnPrefIPv4) + { + Assert.NotNull(DNSInfo.Item2); //IPv4 + Assert.Null(DNSInfo.Item3); //IPv6 + } + else if(ipPreference == CnnPrefIPv6) + { + Assert.Null(DNSInfo.Item2); + Assert.NotNull(DNSInfo.Item3); + } + else + { + Assert.True((DNSInfo.Item2 != null && DNSInfo.Item3 == null) || (DNSInfo.Item2 == null && DNSInfo.Item3 != null)); + } + } + } + + // Azure SQL Server doesn't support dual-stack IPv4 and IPv6 that is going to be supported by end of 2021. + [ConditionalTheory(typeof(DataTestUtility), nameof(DoesHostAddressContainBothIPv4AndIPv6), nameof(IsUsingManagedSNI))] + [InlineData(CnnPrefIPv6)] + [InlineData(CnnPrefIPv4)] + public void ConfigurableIpPreferenceManagedSni(string ipPreference) + => TestCachedConfigurableIpPreference(ipPreference, DNSCachingConnString); + + private void TestCachedConfigurableIpPreference(string ipPreference, string cnnString) + { + using (SqlConnection connection = new SqlConnection(cnnString + ipPreference)) + { + // each successful connection updates the dns cache entry for the data source + connection.Open(); + var SQLFallbackDNSCacheInstance = GetDnsCache(); + + // get the dns cache entry with the given key. parameters[1] will be initialized as the entry + object[] parameters = new object[] { connection.DataSource, null }; + SQLFallbackDNSCacheGetDNSInfo.Invoke(SQLFallbackDNSCacheInstance, parameters); + var dnsCacheEntry = parameters[1]; + + const string AddrIPv4Property = "AddrIPv4"; + const string AddrIPv6Property = "AddrIPv6"; + const string FQDNProperty = "FQDN"; + + Assert.NotNull(dnsCacheEntry); + Assert.Equal(connection.DataSource, GetPropertyValueFromCacheEntry(FQDNProperty, dnsCacheEntry)); + + if (ipPreference == CnnPrefIPv4) + { + Assert.NotNull(GetPropertyValueFromCacheEntry(AddrIPv4Property, dnsCacheEntry)); + Assert.Null(GetPropertyValueFromCacheEntry(AddrIPv6Property, dnsCacheEntry)); + } + else if (ipPreference == CnnPrefIPv6) + { + string ipv6 = GetPropertyValueFromCacheEntry(AddrIPv6Property, dnsCacheEntry); + Assert.NotNull(ipv6); + Assert.Null(GetPropertyValueFromCacheEntry(AddrIPv4Property, dnsCacheEntry)); + } + } + + object GetDnsCache() => + SQLFallbackDNSCacheType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public).GetValue(null); + + string GetPropertyValueFromCacheEntry(string property, object dnsCacheEntry) => + (string)SQLDNSInfoType.GetProperty(property).GetValue(dnsCacheEntry); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs index 33460acb8d..a23efff073 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Reflection; using Xunit; diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json index 4c725d8555..6c631187b5 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json @@ -18,6 +18,11 @@ "SupportsLocalDb": false, "SupportsFileStream": false, "UseManagedSNIOnWindows": false, + "DNSCachingConnString": "", + "DNSCachingServerCR": "", + "DNSCachingServerTR": "", + "IsDNSCachingSupportedCR": false, + "IsDNSCachingSupportedTR": false, "IsAzureSynapse": false, "EnclaveAzureDatabaseConnString": "", "UserManagedIdentityClientId": "" From 5e067c40ae6f87f54dcdfd44d3de6a7488241c62 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Mon, 17 May 2021 16:58:30 -0700 Subject: [PATCH 34/87] Add multi-user key store provider registration support (#1056) --- .../SqlColumnEncryptionKeyStoreProvider.xml | 12 +- .../Microsoft.Data.SqlClient/SqlCommand.xml | 19 ++ .../AzureKeyVaultProvider/Constants.cs | 5 - .../AzureKeyVaultProvider/LocalCache.cs | 96 ++++++++++ ...waysEncrypted.AzureKeyVaultProvider.csproj | 1 + ...qlColumnEncryptionAzureKeyVaultProvider.cs | 169 ++++++++++++----- .../netcore/ref/Microsoft.Data.SqlClient.cs | 2 + .../Microsoft/Data/SqlClient/SqlCommand.cs | 84 ++++++++- .../Microsoft/Data/SqlClient/SqlConnection.cs | 86 ++++----- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 2 +- .../Data/SqlClient/SqlQueryMetadataCache.cs | 2 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 19 +- .../netfx/ref/Microsoft.Data.SqlClient.cs | 2 + .../Microsoft/Data/SqlClient/SqlCommand.cs | 92 ++++++++- .../Microsoft/Data/SqlClient/SqlConnection.cs | 93 +++++----- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 2 +- .../Data/SqlClient/SqlQueryMetadataCache.cs | 2 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 14 +- .../Data/SqlClient/EnclaveDelegate.Crypto.cs | 5 +- .../SqlClient/EnclaveDelegate.NotSupported.cs | 2 +- .../Data/SqlClient/EnclaveDelegate.cs | 9 +- .../SqlColumnEncryptionKeyStoreProvider.cs | 3 + .../Data/SqlClient/SqlSecurityUtility.cs | 175 ++++++++++++------ .../Data/SqlClient/SqlSymmetricKeyCache.cs | 35 +--- .../ExceptionRegisterKeyStoreProvider.cs | 137 ++++++++------ .../AlwaysEncryptedTests/Utility.cs | 4 +- .../ManualTests/AlwaysEncrypted/AKVTests.cs | 48 +++++ .../AlwaysEncrypted/AKVUnitTests.cs | 134 +++++++++++++- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 154 ++++++++++----- tools/props/Versions.props | 1 + ...waysEncrypted.AzureKeyVaultProvider.nuspec | 3 + 31 files changed, 1041 insertions(+), 371 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/LocalCache.cs diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml index 1c24fb735b..2ea4a2522a 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml @@ -1,9 +1,12 @@ - Base class for all key store providers. A custom provider must derive from this class and override its member functions and then register it using SqlConnection.RegisterColumnEncryptionKeyStoreProviders(). For details see, Always Encrypted. + Base class for all key store providers. A custom provider must derive from this class and override its member functions and then register it using + , + or + . + For details see, Always Encrypted. - To be added. Initializes a new instance of the SqlColumnEncryptionKeyStoreProviderClass. @@ -55,5 +58,10 @@ The When implemented in a derived class, the method is expected to return true if the specified signature is valid, or false if the specified signature is not valid. The default implementation throws NotImplementedException. To be added. + + Gets or sets the lifespan of the decrypted column encryption key in the cache. Once the timespan has elapsed, the decrypted column encryption key is discarded and must be revalidated. + Internally, there is a cache of column encryption keys (once they are decrypted). This is useful for rapidly decrypting multiple data values. The default value is 2 hours. Setting this value to zero disables caching. + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index 5cbc8d9ab4..f9e30cc2cb 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -2769,6 +2769,25 @@ You must set the value for this property before the command is executed for it t ]]> + + Dictionary of custom column encryption key providers + Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the or + methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + + A null dictionary was provided. + + -or- + + A string key in the dictionary was null or empty. + + -or- + + An EncryptionKeyStoreProvider value in the dictionary was null. + + + A string key in the dictionary started with "MSSQL_". This prefix is reserved for system providers. + + Gets or sets a value that specifies the diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs index 310f97f944..6e26ac8539 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs @@ -6,11 +6,6 @@ namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider { internal static class Constants { - /// - /// Hashing algorithm used for signing - /// - internal const string HashingAlgorithm = @"RS256"; - /// /// Azure Key Vault Domain Name /// diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/LocalCache.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/LocalCache.cs new file mode 100644 index 0000000000..7af3f5dbef --- /dev/null +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/LocalCache.cs @@ -0,0 +1,96 @@ +// 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 Microsoft.Extensions.Caching.Memory; +using System; +using static System.Math; + +namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider +{ + /// + /// LocalCache is to reuse heavy objects. + /// When performing a heavy creation operation, we will save the result in our cache container. + /// The next time that we need that result, we will pull it from the cache container, instead of performing the heavy operation again. + /// It is used for decrypting CEKs and verifying CMK metadata. Encrypted CEKs and signatures are different every time, even + /// when done with the same key, and should not be cached. + /// + internal class LocalCache + { + /// + /// A simple thread-safe implementation of an in-memory Cache. + /// When the process dies, the cache dies with it. + /// + private readonly MemoryCache _cache; + + private readonly int _maxSize; + + /// + /// Sets an absolute expiration time, relative to now. + /// + internal TimeSpan? TimeToLive { get; set; } + + /// + /// Gets the count of the current entries for diagnostic purposes. + /// + internal int Count => _cache.Count; + + /// + /// Constructs a new LocalCache object. + /// + internal LocalCache(int maxSizeLimit = int.MaxValue) + { + if(maxSizeLimit <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxSizeLimit)); + } + + _maxSize = maxSizeLimit; + _cache = new MemoryCache(new MemoryCacheOptions()); + } + + /// + /// Looks for the cache entry that maps to the value. If it exists (cache hit) it will simply be + /// returned. Otherwise, the delegate function will be invoked to create the value. + /// It will then get stored it in the cache and set the time-to-live before getting returned. + /// + /// The key for the cache entry. + /// The delegate function that will create the cache entry if it does not exist. + /// The cache entry. + internal TValue GetOrCreate(TKey key, Func createItem) + { + if (TimeToLive <= TimeSpan.Zero) + { + return createItem(); + } + + if (!_cache.TryGetValue(key, out TValue cacheEntry)) + { + if (_cache.Count == _maxSize) + { + _cache.Compact(Max(0.10, 1.0 / _maxSize)); + } + + cacheEntry = createItem(); + var cacheEntryOptions = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeToLive + }; + + _cache.Set(key, cacheEntry, cacheEntryOptions); + } + + return cacheEntry; + } + + /// + /// Determines whether the LocalCache contains the specified key. + /// + /// + /// + internal bool Contains(TKey key) + { + return _cache.TryGetValue(key, out _); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj index ae368627fa..5e422ed108 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj @@ -25,5 +25,6 @@ + diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs index d5769ccf80..4a8b8f8aac 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using System.Text; -using System.Threading.Tasks; using Azure.Core; using Azure.Security.KeyVault.Keys.Cryptography; using static Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.Validator; @@ -63,6 +62,33 @@ public class SqlColumnEncryptionAzureKeyVaultProvider : SqlColumnEncryptionKeySt /// public readonly string[] TrustedEndPoints; + /// + /// A cache of column encryption keys (once they are decrypted). This is useful for rapidly decrypting multiple data values. + /// + private readonly LocalCache _columnEncryptionKeyCache = new() { TimeToLive = TimeSpan.FromHours(2) }; + + /// + /// A cache for storing the results of signature verification of column master key metadata. + /// + private readonly LocalCache, bool> _columnMasterKeyMetadataSignatureVerificationCache = + new(maxSizeLimit: 2000) { TimeToLive = TimeSpan.FromDays(10) }; + + /// + /// Gets or sets the lifespan of the decrypted column encryption key in the cache. + /// Once the timespan has elapsed, the decrypted column encryption key is discarded + /// and must be revalidated. + /// + /// + /// Internally, there is a cache of column encryption keys (once they are decrypted). + /// This is useful for rapidly decrypting multiple data values. The default value is 2 hours. + /// Setting the to zero disables caching. + /// + public override TimeSpan? ColumnEncryptionKeyCacheTtl + { + get => _columnEncryptionKeyCache.TimeToLive; + set => _columnEncryptionKeyCache.TimeToLive = value; + } + #endregion #region Constructors @@ -130,10 +156,16 @@ public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool al { ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: true); - // Also validates key is of RSA type. - KeyCryptographer.AddKey(masterKeyPath); - byte[] message = CompileMasterKeyMetadata(masterKeyPath, allowEnclaveComputations); - return KeyCryptographer.VerifyData(message, signature, masterKeyPath); + var key = Tuple.Create(masterKeyPath, allowEnclaveComputations, ToHexString(signature)); + return GetOrCreateSignatureVerificationResult(key, VerifyColumnMasterKeyMetadata); + + bool VerifyColumnMasterKeyMetadata() + { + // Also validates key is of RSA type. + KeyCryptographer.AddKey(masterKeyPath); + byte[] message = CompileMasterKeyMetadata(masterKeyPath, allowEnclaveComputations); + return KeyCryptographer.VerifyData(message, signature, masterKeyPath); + } } /// @@ -153,58 +185,62 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e ValidateNotEmpty(encryptedColumnEncryptionKey, nameof(encryptedColumnEncryptionKey)); ValidateVersionByte(encryptedColumnEncryptionKey[0], s_firstVersion[0]); - // Also validates whether the key is RSA one or not and then get the key size - KeyCryptographer.AddKey(masterKeyPath); + return GetOrCreateColumnEncryptionKey(ToHexString(encryptedColumnEncryptionKey), DecryptEncryptionKey); - int keySizeInBytes = KeyCryptographer.GetKeySize(masterKeyPath); + byte[] DecryptEncryptionKey() + { + // Also validates whether the key is RSA one or not and then get the key size + KeyCryptographer.AddKey(masterKeyPath); - // Get key path length - int currentIndex = s_firstVersion.Length; - ushort keyPathLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); - currentIndex += sizeof(ushort); + int keySizeInBytes = KeyCryptographer.GetKeySize(masterKeyPath); - // Get ciphertext length - ushort cipherTextLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); - currentIndex += sizeof(ushort); + // Get key path length + int currentIndex = s_firstVersion.Length; + ushort keyPathLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); + currentIndex += sizeof(ushort); - // Skip KeyPath - // KeyPath exists only for troubleshooting purposes and doesnt need validation. - currentIndex += keyPathLength; + // Get ciphertext length + ushort cipherTextLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); + currentIndex += sizeof(ushort); - // validate the ciphertext length - if (cipherTextLength != keySizeInBytes) - { - throw ADP.InvalidCipherTextLength(cipherTextLength, keySizeInBytes, masterKeyPath); - } + // Skip KeyPath + // KeyPath exists only for troubleshooting purposes and doesnt need validation. + currentIndex += keyPathLength; - // Validate the signature length - int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; - if (signatureLength != keySizeInBytes) - { - throw ADP.InvalidSignatureLengthTemplate(signatureLength, keySizeInBytes, masterKeyPath); - } + // validate the ciphertext length + if (cipherTextLength != keySizeInBytes) + { + throw ADP.InvalidCipherTextLength(cipherTextLength, keySizeInBytes, masterKeyPath); + } + + // Validate the signature length + int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; + if (signatureLength != keySizeInBytes) + { + throw ADP.InvalidSignatureLengthTemplate(signatureLength, keySizeInBytes, masterKeyPath); + } - // Get ciphertext - byte[] cipherText = encryptedColumnEncryptionKey.Skip(currentIndex).Take(cipherTextLength).ToArray(); - currentIndex += cipherTextLength; + // Get ciphertext + byte[] cipherText = encryptedColumnEncryptionKey.Skip(currentIndex).Take(cipherTextLength).ToArray(); + currentIndex += cipherTextLength; - // Get signature - byte[] signature = encryptedColumnEncryptionKey.Skip(currentIndex).Take(signatureLength).ToArray(); + // Get signature + byte[] signature = encryptedColumnEncryptionKey.Skip(currentIndex).Take(signatureLength).ToArray(); - // Compute the message to validate the signature - byte[] message = encryptedColumnEncryptionKey.Take(encryptedColumnEncryptionKey.Length - signatureLength).ToArray(); + // Compute the message to validate the signature + byte[] message = encryptedColumnEncryptionKey.Take(encryptedColumnEncryptionKey.Length - signatureLength).ToArray(); - if (null == message) - { - throw ADP.NullHashFound(); - } + if (null == message) + { + throw ADP.NullHashFound(); + } - if (!KeyCryptographer.VerifyData(message, signature, masterKeyPath)) - { - throw ADP.InvalidSignatureTemplate(masterKeyPath); + if (!KeyCryptographer.VerifyData(message, signature, masterKeyPath)) + { + throw ADP.InvalidSignatureTemplate(masterKeyPath); + } + return KeyCryptographer.UnwrapKey(s_keyWrapAlgorithm, cipherText, masterKeyPath); } - - return KeyCryptographer.UnwrapKey(s_keyWrapAlgorithm, cipherText, masterKeyPath); } /// @@ -310,6 +346,49 @@ private byte[] CompileMasterKeyMetadata(string masterKeyPath, bool allowEnclaveC return Encoding.Unicode.GetBytes(masterkeyMetadata.ToLowerInvariant()); } + /// + /// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string representation. + /// + /// An array of bytes to convert. + /// A string of hexadecimal characters + /// + /// Produces a string of hexadecimal character pairs preceded with "0x", where each pair represents the corresponding element in value; for example, "0x7F2C4A00". + /// + private string ToHexString(byte[] source) + { + if (source is null) + { + return null; + } + + return "0x" + BitConverter.ToString(source).Replace("-", ""); + } + + /// + /// Returns the cached decrypted column encryption key, or unwraps the encrypted column encryption key if not present. + /// + /// Encrypted Column Encryption Key + /// The delegate function that will decrypt the encrypted column encryption key. + /// The decrypted column encryption key. + /// + /// + /// + private byte[] GetOrCreateColumnEncryptionKey(string encryptedColumnEncryptionKey, Func createItem) + { + return _columnEncryptionKeyCache.GetOrCreate(encryptedColumnEncryptionKey, createItem); + } + + /// + /// Returns the cached signature verification result, or proceeds to verify if not present. + /// + /// The encryptionKeyId, allowEnclaveComputations and hexadecimal signature. + /// The delegate function that will perform the verification. + /// + private bool GetOrCreateSignatureVerificationResult(Tuple keyInformation, Func createItem) + { + return _columnMasterKeyMetadataSignatureVerificationCache.GetOrCreate(keyInformation, createItem); + } + #endregion } } diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 17bc6c59c4..824705550b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -609,6 +609,8 @@ public sealed partial class SqlCommand : System.Data.Common.DbCommand, System.IC [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public Microsoft.Data.Sql.SqlNotificationRequest Notification { get { throw null; } set { } } + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(System.Collections.Generic.IDictionary customProviders) { } /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Content)] public void ResetCommandTimeout() { } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 13a66ac1c4..e520bf7e6e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -192,6 +192,14 @@ internal bool ShouldUseEnclaveBasedWorkflow get { return !string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) && IsColumnEncryptionEnabled; } } + /// + /// Per-command custom providers. It can be provided by the user and can be set more than once. + /// + private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + + internal bool HasColumnEncryptionKeyStoreProvidersRegistered => + _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; + // Cached info for async executions private sealed class CachedAsyncState { @@ -2774,6 +2782,74 @@ private Task InternalExecuteXmlReaderAsync(CancellationToken cancella return returnedTask; } + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(IDictionary customProviders) + { + ValidateCustomProviders(customProviders); + + // Create a temporary dictionary and then add items from the provided dictionary. + // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs + // in the provided customerProviders dictionary. + Dictionary customColumnEncryptionKeyStoreProviders = + new(customProviders, StringComparer.OrdinalIgnoreCase); + + _customColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; + } + + private void ValidateCustomProviders(IDictionary customProviders) + { + // Throw when the provided dictionary is null. + if (customProviders is null) + { + throw SQL.NullCustomKeyStoreProviderDictionary(); + } + + // Validate that custom provider list doesn't contain any of system provider list + foreach (string key in customProviders.Keys) + { + // Validate the provider name + // + // Check for null or empty + if (string.IsNullOrWhiteSpace(key)) + { + throw SQL.EmptyProviderName(); + } + + // Check if the name starts with MSSQL_, since this is reserved namespace for system providers. + if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase)) + { + throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix); + } + + // Validate the provider value + if (customProviders[key] is null) + { + throw SQL.NullProviderValue(key); + } + } + } + + /// + /// This function walks through the registered custom column encryption key store providers and returns an object if found. + /// + /// Provider Name to be searched in custom provider dictionary. + /// If the provider is found, initializes the corresponding SqlColumnEncryptionKeyStoreProvider instance. + /// true if the provider is found, else returns false + internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) + { + Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid"); + return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); + } + + /// + /// This function returns a list of the names of the custom providers currently registered. + /// + /// Combined list of provider names + internal List GetColumnEncryptionCustomKeyStoreProvidersNames() + { + return _customColumnEncryptionKeyStoreProviders.Keys.ToList(); + } + // If the user part is quoted, remove first and last brackets and then unquote any right square // brackets in the procedure. This is a very simple parser that performs no validation. As // with the function below, ideally we should have support from the server for this. @@ -4084,7 +4160,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi ds.GetBytes((int)DescribeParameterEncryptionResultSet1.KeySignature, 0, keySignature, 0, keySignatureLength); } - SqlSecurityUtility.VerifyColumnMasterKeySignature(providerName, keyPath, isRequestedByEnclave, keySignature, _activeConnection); + SqlSecurityUtility.VerifyColumnMasterKeySignature(providerName, keyPath, isRequestedByEnclave, keySignature, _activeConnection, this); int requestedKey = currentOrdinal; SqlTceCipherInfoEntry cipherInfo; @@ -4188,7 +4264,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi // Decrypt the symmetric key.(This will also validate and throw if needed). Debug.Assert(_activeConnection != null, @"_activeConnection should not be null"); - SqlSecurityUtility.DecryptSymmetricKey(sqlParameter.CipherMetadata, _activeConnection); + SqlSecurityUtility.DecryptSymmetricKey(sqlParameter.CipherMetadata, _activeConnection, this); // This is effective only for BatchRPCMode even though we set it for non-BatchRPCMode also, // since for non-BatchRPCMode mode, paramoptions gets thrown away and reconstructed in BuildExecuteSql. @@ -4519,7 +4595,7 @@ private void GenerateEnclavePackage() { EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(attestationProtocol, keysToBeSentToEnclave, - this.CommandText, enclaveType, enclaveSessionParameters, _activeConnection); + this.CommandText, enclaveType, enclaveSessionParameters, _activeConnection, this); } catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { @@ -5273,7 +5349,7 @@ internal void OnReturnValue(SqlReturnValue rec, TdsParserStateObject stateObj) // Get the key information from the parameter and decrypt the value. rec.cipherMD.EncryptionInfo = thisParam.CipherMetadata.EncryptionInfo; - byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(rec.value.ByteArray, rec.cipherMD, _activeConnection); + byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(rec.value.ByteArray, rec.cipherMD, _activeConnection, this); if (unencryptedBytes != null) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index d89f841b48..897fc37b5a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -71,19 +71,26 @@ private enum CultureCheckState : uint // using SqlConnection.Open() method. internal bool _applyTransientFaultHandling = false; + // status of invariant culture environment check + private static CultureCheckState _cultureCheckState; + // System column encryption key store providers are added by default private static readonly Dictionary s_systemColumnEncryptionKeyStoreProviders - = new Dictionary(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase) - { - {SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider()}, - {SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider()}, - {SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider()} - }; + = new(capacity: 3, comparer: StringComparer.OrdinalIgnoreCase) + { + { SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider() }, + { SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider() }, + { SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider() } + }; + + /// Instance-level list of custom key store providers. It can be set more than once by the user. + private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + + internal bool HasColumnEncryptionKeyStoreProvidersRegistered => + _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; // Lock to control setting of s_globalCustomColumnEncryptionKeyStoreProviders - private static readonly object s_globalCustomColumnEncryptionKeyProvidersLock = new object(); - // status of invariant culture environment check - private static CultureCheckState _cultureCheckState; + private static readonly object s_globalCustomColumnEncryptionKeyProvidersLock = new(); /// /// Global custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary. @@ -91,10 +98,7 @@ private enum CultureCheckState : uint /// private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; - /// - /// Per-connection custom providers. It can be provided by the user and can be set more than once. - /// - private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + private static string s_akvProviderName = "AZURE_KEY_VAULT"; /// /// Dictionary object holding trusted key paths for various SQL Servers. @@ -102,7 +106,7 @@ private enum CultureCheckState : uint /// IList contains a list of trusted key paths. /// private static readonly ConcurrentDictionary> _ColumnEncryptionTrustedMasterKeyPaths - = new ConcurrentDictionary>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, + = new(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 1, comparer: StringComparer.OrdinalIgnoreCase); @@ -222,16 +226,17 @@ private SqlConnection(SqlConnection connection) /// /// This function walks through both system and custom column encryption key store providers and returns an object if found. /// - /// Provider Name to be searched in System Provider diction and Custom provider dictionary. - /// If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance. - /// The connection requiring the provider + /// Provider Name to be searched in System Provider dictionary and Custom provider dictionary. + /// If the provider is found, initializes the corresponding SqlColumnEncryptionKeyStoreProvider instance. /// true if the provider is found, else returns false - internal static bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider, SqlConnection connection) + internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) { Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid"); - // Initialize the out parameter - columnKeyStoreProvider = null; + if (HasColumnEncryptionKeyStoreProvidersRegistered) + { + return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); + } // Search in the sytem provider list. if (s_systemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider)) @@ -239,17 +244,10 @@ internal static bool TryGetColumnEncryptionKeyStoreProvider(string providerName, return true; } - // instance-level custom provider cache takes precedence over global cache - if (connection._customColumnEncryptionKeyStoreProviders != null && - connection._customColumnEncryptionKeyStoreProviders.Count > 0) - { - return connection._customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); - } - lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false - if (s_globalCustomColumnEncryptionKeyStoreProviders == null) + if (s_globalCustomColumnEncryptionKeyStoreProviders is null) { return false; } @@ -263,7 +261,7 @@ internal static bool TryGetColumnEncryptionKeyStoreProvider(string providerName, /// This function returns a list of system providers currently supported by this driver. /// /// Combined list of provider names - internal static List GetColumnEncryptionSystemKeyStoreProviders() + internal static List GetColumnEncryptionSystemKeyStoreProvidersNames() { return s_systemColumnEncryptionKeyStoreProviders.Keys.ToList(); } @@ -272,16 +270,15 @@ internal static List GetColumnEncryptionSystemKeyStoreProviders() /// This function returns a list of the names of the custom providers currently registered. If the /// instance-level cache is not empty, that cache is used, else the global cache is used. /// - /// The connection requiring the provider /// Combined list of provider names - internal static List GetColumnEncryptionCustomKeyStoreProviders(SqlConnection connection) + internal List GetColumnEncryptionCustomKeyStoreProvidersNames() { - if (connection._customColumnEncryptionKeyStoreProviders != null && - connection._customColumnEncryptionKeyStoreProviders.Count > 0) + if (_customColumnEncryptionKeyStoreProviders is not null && + _customColumnEncryptionKeyStoreProviders.Count > 0) { - return connection._customColumnEncryptionKeyStoreProviders.Keys.ToList(); + return _customColumnEncryptionKeyStoreProviders.Keys.ToList(); } - if (s_globalCustomColumnEncryptionKeyStoreProviders != null) + if (s_globalCustomColumnEncryptionKeyStoreProviders is not null) { return s_globalCustomColumnEncryptionKeyStoreProviders.Keys.ToList(); } @@ -308,16 +305,21 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary customColumnEncryptionKeyStoreProviders = - new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); + new(customProviders, StringComparer.OrdinalIgnoreCase); // Set the dictionary to the ReadOnly dictionary. s_globalCustomColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; @@ -333,7 +335,7 @@ public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customColumnEncryptionKeyStoreProviders = - new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); + new(customProviders, StringComparer.OrdinalIgnoreCase); // Set the dictionary to the ReadOnly dictionary. // This method can be called more than once. Re-registering a new collection will replace the @@ -344,7 +346,7 @@ public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) { // Throw when the provided dictionary is null. - if (customProviders == null) + if (customProviders is null) { throw SQL.NullCustomKeyStoreProviderDictionary(); } @@ -367,7 +369,7 @@ private static void ValidateCustomProviders(IDictionary public override Task OpenAsync(CancellationToken cancellationToken) - => IsRetryEnabled ? - InternalOpenWithRetryAsync(cancellationToken) : + => IsRetryEnabled ? + InternalOpenWithRetryAsync(cancellationToken) : InternalOpenAsync(cancellationToken); private Task InternalOpenAsync(CancellationToken cancellationToken) 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 f94f126b80..a8a02be484 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 @@ -3798,7 +3798,7 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) // can read it out of order if (!_parser.TryReadSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, (int)dataLength, _stateObj, _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, - columnMetaData.column)) + columnMetaData.column, _command)) { // will read UDTs as VARBINARY. return false; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs index 94a7661e54..9ddc6e767a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs @@ -119,7 +119,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) // In this case, just fail the cache lookup. try { - SqlSecurityUtility.DecryptSymmetricKey(cipherMdCopy, sqlCommand.Connection); + SqlSecurityUtility.DecryptSymmetricKey(cipherMdCopy, sqlCommand.Connection, sqlCommand); } catch (Exception ex) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 9e26aa375f..210b5944cf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -4211,7 +4211,7 @@ internal bool TryProcessReturnValue(int length, TdsParserStateObject stateObj, o _connHandler != null && _connHandler.ConnectionOptions != null && _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled)) { - col.cipherMD = new SqlCipherMetadata(cipherTable !=null ? (SqlTceCipherInfoEntry)cipherTable[index] : null, + col.cipherMD = new SqlCipherMetadata(cipherTable != null ? (SqlTceCipherInfoEntry)cipherTable[index] : null, index, cipherAlgorithmId: cipherAlgorithmId, cipherAlgorithmName: cipherAlgorithmName, @@ -5888,7 +5888,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt return true; } - internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, TdsParserStateObject stateObj, SqlCommandColumnEncryptionSetting columnEncryptionOverride, string columnName) + internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, TdsParserStateObject stateObj, SqlCommandColumnEncryptionSetting columnEncryptionOverride, string columnName, SqlCommand command = null) { bool isPlp = md.metaType.IsPlp; byte tdsType = md.tdsType; @@ -5951,7 +5951,7 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T try { // CipherInfo is present, decrypt and read - byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(b, md.cipherMD, _connHandler.Connection); + byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(b, md.cipherMD, _connHandler.Connection, command); if (unencryptedBytes != null) { @@ -9023,7 +9023,7 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques throw ADP.VersionDoesNotSupportDataType(mt.TypeName); } - Task writeParamTask = TDSExecuteRPCAddParameter(stateObj, param, mt, options); + Task writeParamTask = TDSExecuteRPCAddParameter(stateObj, param, mt, options, cmd); if (!sync) { @@ -9146,7 +9146,7 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques } } - private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParameter param, MetaType mt, byte options) + private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParameter param, MetaType mt, byte options, SqlCommand command) { int tempLen; object value = null; @@ -9271,7 +9271,7 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet } Debug.Assert(serializedValue != null, "serializedValue should not be null in TdsExecuteRPC."); - encryptedValue = SqlSecurityUtility.EncryptWithKey(serializedValue, param.CipherMetadata, _connHandler.Connection); + encryptedValue = SqlSecurityUtility.EncryptWithKey(serializedValue, param.CipherMetadata, _connHandler.Connection, command); } catch (Exception e) { @@ -10138,7 +10138,7 @@ internal Task WriteBulkCopyDone(TdsParserStateObject stateObj) /// decrypt the CEK and keep it ready for encryption. /// /// - internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlConnection connection) + internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlConnection connection, SqlCommand command = null) { if (IsColumnEncryptionSupported && ShouldEncryptValuesForBulkCopy()) { @@ -10149,7 +10149,7 @@ internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlCo _SqlMetaData md = metadataCollection[col]; if (md.isEncrypted) { - SqlSecurityUtility.DecryptSymmetricKey(md.cipherMD, connection); + SqlSecurityUtility.DecryptSymmetricKey(md.cipherMD, connection, command); } } } @@ -10499,7 +10499,8 @@ internal object EncryptColumnValue(object value, SqlMetaDataPriv metadata, strin return SqlSecurityUtility.EncryptWithKey( serializedValue, metadata.cipherMD, - _connHandler.Connection); + _connHandler.Connection, + null); } internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsParserStateObject stateObj, bool isSqlType, bool isDataFeed, bool isNull) diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 7e35b64c04..64f25ac54f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -643,6 +643,8 @@ public sealed partial class SqlCommand : System.Data.Common.DbCommand, System.IC /// [System.ComponentModel.DefaultValueAttribute(true)] public bool NotificationAutoEnlist { get { throw null; } set { } } + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(System.Collections.Generic.IDictionary customProviders) { } /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Content)] public void ResetCommandTimeout() { } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index e962a5695c..93002d88e4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -164,6 +164,14 @@ internal bool ShouldUseEnclaveBasedWorkflow get { return !string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) && IsColumnEncryptionEnabled; } } + /// + /// Per-command custom providers. It can be provided by the user and can be set more than once. + /// + private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + + internal bool HasColumnEncryptionKeyStoreProvidersRegistered => + _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; + // Cached info for async executions private sealed class CachedAsyncState { @@ -2391,7 +2399,7 @@ private SqlDataReader ExecuteReaderWithRetry(CommandBehavior behavior, string me { statistics = SqlStatistics.StartTimer(Statistics); return IsRetryEnabled ? - ExecuteReaderWithRetry(CommandBehavior.Default, ADP.ExecuteReader) : + ExecuteReaderWithRetry(CommandBehavior.Default, ADP.ExecuteReader) : ExecuteReader(CommandBehavior.Default, ADP.ExecuteReader); } finally @@ -2410,7 +2418,7 @@ private SqlDataReader ExecuteReaderWithRetry(CommandBehavior behavior, string me try { return IsRetryEnabled ? - ExecuteReaderWithRetry(behavior, ADP.ExecuteReader) : + ExecuteReaderWithRetry(behavior, ADP.ExecuteReader) : ExecuteReader(behavior, ADP.ExecuteReader); } finally @@ -2921,8 +2929,8 @@ private Task InternalExecuteNonQueryWithRetryAsync(CancellationToken cancel /// public override Task ExecuteNonQueryAsync(CancellationToken cancellationToken) - => IsRetryEnabled ? - InternalExecuteNonQueryWithRetryAsync(cancellationToken) : + => IsRetryEnabled ? + InternalExecuteNonQueryWithRetryAsync(cancellationToken) : InternalExecuteNonQueryAsync(cancellationToken); private Task InternalExecuteNonQueryAsync(CancellationToken cancellationToken) @@ -3219,6 +3227,74 @@ private Task InternalExecuteXmlReaderAsync(CancellationToken cancella return returnedTask; } + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(IDictionary customProviders) + { + ValidateCustomProviders(customProviders); + + // Create a temporary dictionary and then add items from the provided dictionary. + // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs + // in the provided customerProviders dictionary. + Dictionary customColumnEncryptionKeyStoreProviders = + new(customProviders, StringComparer.OrdinalIgnoreCase); + + _customColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; + } + + private void ValidateCustomProviders(IDictionary customProviders) + { + // Throw when the provided dictionary is null. + if (customProviders is null) + { + throw SQL.NullCustomKeyStoreProviderDictionary(); + } + + // Validate that custom provider list doesn't contain any of system provider list + foreach (string key in customProviders.Keys) + { + // Validate the provider name + // + // Check for null or empty + if (string.IsNullOrWhiteSpace(key)) + { + throw SQL.EmptyProviderName(); + } + + // Check if the name starts with MSSQL_, since this is reserved namespace for system providers. + if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase)) + { + throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix); + } + + // Validate the provider value + if (customProviders[key] is null) + { + throw SQL.NullProviderValue(key); + } + } + } + + /// + /// This function walks through the registered custom column encryption key store providers and returns an object if found. + /// + /// Provider Name to be searched in custom provider dictionary. + /// If the provider is found, initializes the corresponding SqlColumnEncryptionKeyStoreProvider instance. + /// true if the provider is found, else returns false + internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) + { + Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid"); + return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); + } + + /// + /// This function returns a list of the names of the custom providers currently registered. + /// + /// Combined list of provider names + internal List GetColumnEncryptionCustomKeyStoreProvidersNames() + { + return _customColumnEncryptionKeyStoreProviders.Keys.ToList(); + } + // If the user part is quoted, remove first and last brackets and then unquote any right square // brackets in the procedure. This is a very simple parser that performs no validation. As // with the function below, ideally we should have support from the server for this. @@ -4693,7 +4769,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi ds.GetBytes((int)DescribeParameterEncryptionResultSet1.KeySignature, 0, keySignature, 0, keySignatureLength); } - SqlSecurityUtility.VerifyColumnMasterKeySignature(providerName, keyPath, isRequestedByEnclave, keySignature, _activeConnection); + SqlSecurityUtility.VerifyColumnMasterKeySignature(providerName, keyPath, isRequestedByEnclave, keySignature, _activeConnection, this); int requestedKey = currentOrdinal; SqlTceCipherInfoEntry cipherInfo; @@ -4799,7 +4875,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi // Decrypt the symmetric key.(This will also validate and throw if needed). Debug.Assert(_activeConnection != null, @"_activeConnection should not be null"); - SqlSecurityUtility.DecryptSymmetricKey(sqlParameter.CipherMetadata, _activeConnection); + SqlSecurityUtility.DecryptSymmetricKey(sqlParameter.CipherMetadata, _activeConnection, this); // This is effective only for BatchRPCMode even though we set it for non-BatchRPCMode also, // since for non-BatchRPCMode mode, paramoptions gets thrown away and reconstructed in BuildExecuteSql. @@ -5169,7 +5245,7 @@ private void GenerateEnclavePackage() { EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(attestationProtocol, keysToBeSentToEnclave, - this.CommandText, enclaveType, enclaveSessionParameters, _activeConnection); + this.CommandText, enclaveType, enclaveSessionParameters, _activeConnection, this); } catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { @@ -6144,7 +6220,7 @@ internal void OnReturnValue(SqlReturnValue rec, TdsParserStateObject stateObj) // Get the key information from the parameter and decrypt the value. rec.cipherMD.EncryptionInfo = thisParam.CipherMetadata.EncryptionInfo; - byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(rec.value.ByteArray, rec.cipherMD, _activeConnection); + byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(rec.value.ByteArray, rec.cipherMD, _activeConnection, this); if (unencryptedBytes != null) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 3c9a6a4a1a..427e5f71f4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -53,13 +53,13 @@ internal bool ForceNewConnection static private readonly object EventInfoMessage = new object(); // System column encryption key store providers are added by default - static private readonly Dictionary s_systemColumnEncryptionKeyStoreProviders - = new Dictionary(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase) - { - {SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider()}, - {SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider()}, - {SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider()} - }; + private static Dictionary s_systemColumnEncryptionKeyStoreProviders + = new(capacity: 3, comparer: StringComparer.OrdinalIgnoreCase) + { + { SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider() }, + { SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider() }, + { SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider() } + }; /// /// Global custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary. @@ -67,11 +67,16 @@ internal bool ForceNewConnection /// private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; + private static string s_akvProviderName = "AZURE_KEY_VAULT"; + /// Instance-level list of custom key store providers. It can be set more than once by the user. private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + internal bool HasColumnEncryptionKeyStoreProvidersRegistered => + _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; + // Lock to control setting of s_globalCustomColumnEncryptionKeyStoreProviders - private static readonly object s_globalCustomColumnEncryptionKeyProvidersLock = new object(); + private static readonly object s_globalCustomColumnEncryptionKeyProvidersLock = new(); /// /// Dictionary object holding trusted key paths for various SQL Servers. @@ -79,7 +84,7 @@ internal bool ForceNewConnection /// IList contains a list of trusted key paths. /// static private readonly ConcurrentDictionary> _ColumnEncryptionTrustedMasterKeyPaths - = new ConcurrentDictionary>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, + = new(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 1, comparer: StringComparer.OrdinalIgnoreCase); @@ -89,13 +94,7 @@ internal bool ForceNewConnection ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data), ResDescriptionAttribute(StringsHelper.ResourceNames.TCE_SqlConnection_TrustedColumnMasterKeyPaths), ] - static public IDictionary> ColumnEncryptionTrustedMasterKeyPaths - { - get - { - return _ColumnEncryptionTrustedMasterKeyPaths; - } - } + public static IDictionary> ColumnEncryptionTrustedMasterKeyPaths => _ColumnEncryptionTrustedMasterKeyPaths; /// /// Defines whether query metadata caching is enabled. @@ -133,14 +132,8 @@ static public bool ColumnEncryptionQueryMetadataCacheEnabled ] static public TimeSpan ColumnEncryptionKeyCacheTtl { - get - { - return _ColumnEncryptionKeyCacheTtl; - } - set - { - _ColumnEncryptionKeyCacheTtl = value; - } + get => _ColumnEncryptionKeyCacheTtl; + set => _ColumnEncryptionKeyCacheTtl = value; } /// @@ -151,16 +144,21 @@ static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary customColumnEncryptionKeyStoreProviders = - new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); + new(customProviders, StringComparer.OrdinalIgnoreCase); // Set the dictionary to the ReadOnly dictionary. s_globalCustomColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; @@ -176,7 +174,7 @@ public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customColumnEncryptionKeyStoreProviders = - new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); + new(customProviders, StringComparer.OrdinalIgnoreCase); // Set the dictionary to the ReadOnly dictionary. // This method can be called more than once. Re-registering a new collection will replace the @@ -187,7 +185,7 @@ public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) { // Throw when the provided dictionary is null. - if (customProviders == null) + if (customProviders is null) { throw SQL.NullCustomKeyStoreProviderDictionary(); } @@ -209,7 +207,7 @@ private static void ValidateCustomProviders(IDictionary /// Provider Name to be searched in System Provider diction and Custom provider dictionary. /// If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance. - /// The connection requiring the provider /// true if the provider is found, else returns false - static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider, SqlConnection connection) + internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) { Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid"); - // Initialize the out parameter - columnKeyStoreProvider = null; - - // Search in the sytem provider list. - if (s_systemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider)) + if (HasColumnEncryptionKeyStoreProvidersRegistered) { - return true; + return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); } - // instance-level custom provider cache takes precedence over global cache - if (connection._customColumnEncryptionKeyStoreProviders != null && - connection._customColumnEncryptionKeyStoreProviders.Count > 0) + // Search in the system provider list. + if (s_systemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider)) { - return connection._customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); + return true; } lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false - if (s_globalCustomColumnEncryptionKeyStoreProviders == null) + if (s_globalCustomColumnEncryptionKeyStoreProviders is null) { return false; } @@ -260,7 +252,7 @@ static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, /// This function returns a list of system providers currently supported by this driver. /// /// Combined list of provider names - internal static List GetColumnEncryptionSystemKeyStoreProviders() + internal static List GetColumnEncryptionSystemKeyStoreProvidersNames() { return s_systemColumnEncryptionKeyStoreProviders.Keys.ToList(); } @@ -269,16 +261,15 @@ internal static List GetColumnEncryptionSystemKeyStoreProviders() /// This function returns a list of the names of the custom providers currently registered. If the /// instance-level cache is not empty, that cache is used, else the global cache is used. /// - /// The connection requiring the provider /// Combined list of provider names - internal static List GetColumnEncryptionCustomKeyStoreProviders(SqlConnection connection) + internal List GetColumnEncryptionCustomKeyStoreProvidersNames() { - if (connection._customColumnEncryptionKeyStoreProviders != null && - connection._customColumnEncryptionKeyStoreProviders.Count > 0) + if (_customColumnEncryptionKeyStoreProviders is not null && + _customColumnEncryptionKeyStoreProviders.Count > 0) { - return connection._customColumnEncryptionKeyStoreProviders.Keys.ToList(); + return _customColumnEncryptionKeyStoreProviders.Keys.ToList(); } - if (s_globalCustomColumnEncryptionKeyStoreProviders != null) + if (s_globalCustomColumnEncryptionKeyStoreProviders is not null) { return s_globalCustomColumnEncryptionKeyStoreProviders.Keys.ToList(); } @@ -1898,8 +1889,8 @@ private Task InternalOpenWithRetryAsync(CancellationToken cancellationToken) /// public override Task OpenAsync(CancellationToken cancellationToken) - => IsRetryEnabled ? - InternalOpenWithRetryAsync(cancellationToken) : + => IsRetryEnabled ? + InternalOpenWithRetryAsync(cancellationToken) : InternalOpenAsync(cancellationToken); private Task InternalOpenAsync(CancellationToken cancellationToken) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 5bbbc07904..eb54272de3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -4118,7 +4118,7 @@ private bool TryReadColumnData() if (!_parser.TryReadSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, (int)_sharedState._columnDataBytesRemaining, _stateObj, _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, - columnMetaData.column)) + columnMetaData.column, _command)) { // will read UDTs as VARBINARY. return false; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs index a8fa6ae6bc..3b31138cb4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs @@ -121,7 +121,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) // In this case, just fail the cache lookup. try { - SqlSecurityUtility.DecryptSymmetricKey(cipherMdCopy, sqlCommand.Connection); + SqlSecurityUtility.DecryptSymmetricKey(cipherMdCopy, sqlCommand.Connection, sqlCommand); } catch (Exception ex) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 78015db428..84aa07f196 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6670,7 +6670,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt int length, TdsParserStateObject stateObj, SqlCommandColumnEncryptionSetting columnEncryptionOverride, - string columnName) + string columnName, + SqlCommand command = null) { bool isPlp = md.metaType.IsPlp; byte tdsType = md.tdsType; @@ -6733,7 +6734,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt try { // CipherInfo is present, decrypt and read - byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(b, md.cipherMD, _connHandler.Connection); + byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(b, md.cipherMD, _connHandler.Connection, command); if (unencryptedBytes != null) { @@ -10034,7 +10035,7 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques } Debug.Assert(serializedValue != null, "serializedValue should not be null in TdsExecuteRPC."); - encryptedValue = SqlSecurityUtility.EncryptWithKey(serializedValue, param.CipherMetadata, _connHandler.Connection); + encryptedValue = SqlSecurityUtility.EncryptWithKey(serializedValue, param.CipherMetadata, _connHandler.Connection, cmd); } catch (Exception e) { @@ -11066,7 +11067,7 @@ internal Task WriteBulkCopyDone(TdsParserStateObject stateObj) /// decrypt the CEK and keep it ready for encryption. /// /// - internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlConnection connection) + internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlConnection connection, SqlCommand command = null) { if (_serverSupportsColumnEncryption && ShouldEncryptValuesForBulkCopy()) { @@ -11077,7 +11078,7 @@ internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlCo _SqlMetaData md = metadataCollection[col]; if (md.isEncrypted) { - SqlSecurityUtility.DecryptSymmetricKey(md.cipherMD, connection); + SqlSecurityUtility.DecryptSymmetricKey(md.cipherMD, connection, command); } } } @@ -11438,7 +11439,8 @@ internal object EncryptColumnValue(object value, SqlMetaDataPriv metadata, strin return SqlSecurityUtility.EncryptWithKey( serializedValue, metadata.cipherMD, - _connHandler.Connection); + _connHandler.Connection, + null); } internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsParserStateObject stateObj, bool isSqlType, bool isDataFeed, bool isNull) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs index 21c91725a5..b679414cbe 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs @@ -130,8 +130,9 @@ private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttes /// enclave type /// The set of parameters required for enclave session. /// connection executing the query + /// command executing the query /// - internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysToBeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection) + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysToBeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection, SqlCommand command) { SqlEnclaveSession sqlEnclaveSession; long counter; @@ -155,7 +156,7 @@ internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol throw new RetryableEnclaveQueryExecutionException(e.Message, e); } - List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysToBeSentToEnclave, enclaveSessionParameters.ServerName, connection); + List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysToBeSentToEnclave, enclaveSessionParameters.ServerName, connection, command); byte[] queryStringHashBytes = ComputeQueryStringHash(queryText); byte[] keyBytePackage = GenerateBytePackageForKeys(counter, queryStringHashBytes, decryptedKeysToBeSentToEnclave); byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs index 814840c33d..19d1d1f58f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs @@ -36,7 +36,7 @@ internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProt throw new PlatformNotSupportedException(); } - internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection) + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection, SqlCommand command) { throw new PlatformNotSupportedException(); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 99471db9e7..dc45b979f3 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -46,14 +46,15 @@ private byte[] GetUintBytes(string enclaveType, int intValue, string variableNam /// Keys that need to sent to the enclave /// /// + /// /// - private List GetDecryptedKeysToBeSentToEnclave(Dictionary keysTobeSentToEnclave, string serverName, SqlConnection connection) + private List GetDecryptedKeysToBeSentToEnclave(Dictionary keysTobeSentToEnclave, string serverName, SqlConnection connection, SqlCommand command) { List decryptedKeysToBeSentToEnclave = new List(); foreach (SqlTceCipherInfoEntry cipherInfo in keysTobeSentToEnclave.Values) { - SqlSecurityUtility.DecryptSymmetricKey(cipherInfo, out SqlClientSymmetricKey sqlClientSymmetricKey, out SqlEncryptionKeyInfo encryptionkeyInfoChosen, connection); + SqlSecurityUtility.DecryptSymmetricKey(cipherInfo, out SqlClientSymmetricKey sqlClientSymmetricKey, out SqlEncryptionKeyInfo encryptionkeyInfoChosen, connection, command); if (sqlClientSymmetricKey == null) { @@ -75,7 +76,7 @@ private List GetDecryptedKeysToBeSentToEnclave(Dictiona new ColumnEncryptionKeyInfo( sqlClientSymmetricKey.RootKey, cipherInfo.ColumnEncryptionKeyValues[0].databaseId, - cipherInfo.ColumnEncryptionKeyValues[0].cekMdVersion, + cipherInfo.ColumnEncryptionKeyValues[0].cekMdVersion, cipherInfo.ColumnEncryptionKeyValues[0].cekId ) ); @@ -150,7 +151,7 @@ private byte[] EncryptBytePackage(byte[] bytePackage, byte[] sessionKey, string { SqlClientSymmetricKey symmetricKey = new SqlClientSymmetricKey(sessionKey); SqlClientEncryptionAlgorithm sqlClientEncryptionAlgorithm = s_sqlAeadAes256CbcHmac256Factory.Create( - symmetricKey, + symmetricKey, SqlClientEncryptionType.Randomized, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName ); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionKeyStoreProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionKeyStoreProvider.cs index e4a9078373..641e09e8ba 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionKeyStoreProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionKeyStoreProvider.cs @@ -10,6 +10,9 @@ namespace Microsoft.Data.SqlClient /// public abstract class SqlColumnEncryptionKeyStoreProvider { + /// + public virtual TimeSpan? ColumnEncryptionKeyCacheTtl { get; set; } = new TimeSpan(0); + /// public abstract byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs index 94cbe34e36..2077590e77 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs @@ -109,7 +109,7 @@ internal static string GetHexString(byte[] input) { Debug.Assert(input != null); - StringBuilder str = new StringBuilder(); + StringBuilder str = new(); foreach (byte b in input) { str.AppendFormat(b.ToString(@"X2")); @@ -167,12 +167,12 @@ private static string GetRegisteredCipherAlgorithmIds() /// /// Encrypts the plaintext. /// - internal static byte[] EncryptWithKey(byte[] plainText, SqlCipherMetadata md, SqlConnection connection) + internal static byte[] EncryptWithKey(byte[] plainText, SqlCipherMetadata md, SqlConnection connection, SqlCommand command) { // Initialize cipherAlgo if not already done. if (!md.IsAlgorithmInitialized()) { - DecryptSymmetricKey(md, connection); + DecryptSymmetricKey(md, connection, command); } Debug.Assert(md.IsAlgorithmInitialized(), "Encryption Algorithm is not initialized"); @@ -204,12 +204,12 @@ internal static string GetBytesAsString(byte[] buff, bool fLast, int countOfByte /// /// Decrypts the ciphertext. /// - internal static byte[] DecryptWithKey(byte[] cipherText, SqlCipherMetadata md, SqlConnection connection) + internal static byte[] DecryptWithKey(byte[] cipherText, SqlCipherMetadata md, SqlConnection connection, SqlCommand command) { // Initialize cipherAlgo if not already done. if (!md.IsAlgorithmInitialized()) { - DecryptSymmetricKey(md, connection); + DecryptSymmetricKey(md, connection, command); } Debug.Assert(md.IsAlgorithmInitialized(), "Decryption Algorithm is not initialized"); @@ -236,21 +236,21 @@ internal static byte[] DecryptWithKey(byte[] cipherText, SqlCipherMetadata md, S /// Decrypts the symmetric key and saves it in metadata. In addition, initializes /// the SqlClientEncryptionAlgorithm for rapid decryption. /// - internal static void DecryptSymmetricKey(SqlCipherMetadata md, SqlConnection connection) + internal static void DecryptSymmetricKey(SqlCipherMetadata md, SqlConnection connection, SqlCommand command) { - Debug.Assert(md != null, "md should not be null in DecryptSymmetricKey."); + Debug.Assert(md is not null, "md should not be null in DecryptSymmetricKey."); SqlClientSymmetricKey symKey = null; SqlEncryptionKeyInfo encryptionkeyInfoChosen = null; - DecryptSymmetricKey(md.EncryptionInfo, out symKey, out encryptionkeyInfoChosen, connection); + DecryptSymmetricKey(md.EncryptionInfo, out symKey, out encryptionkeyInfoChosen, connection, command); // Given the symmetric key instantiate a SqlClientEncryptionAlgorithm object and cache it in metadata md.CipherAlgorithm = null; SqlClientEncryptionAlgorithm cipherAlgorithm = null; string algorithmName = ValidateAndGetEncryptionAlgorithmName(md.CipherAlgorithmId, md.CipherAlgorithmName); // may throw SqlClientEncryptionAlgorithmFactoryList.GetInstance().GetAlgorithm(symKey, md.EncryptionType, algorithmName, out cipherAlgorithm); // will validate algorithm name and type - Debug.Assert(cipherAlgorithm != null); + Debug.Assert(cipherAlgorithm is not null); md.CipherAlgorithm = cipherAlgorithm; md.EncryptionKeyInfo = encryptionkeyInfoChosen; return; @@ -259,26 +259,26 @@ internal static void DecryptSymmetricKey(SqlCipherMetadata md, SqlConnection con /// /// Decrypts the symmetric key and saves it in metadata. /// - internal static void DecryptSymmetricKey(SqlTceCipherInfoEntry sqlTceCipherInfoEntry, out SqlClientSymmetricKey sqlClientSymmetricKey, out SqlEncryptionKeyInfo encryptionkeyInfoChosen, SqlConnection connection) + internal static void DecryptSymmetricKey(SqlTceCipherInfoEntry sqlTceCipherInfoEntry, out SqlClientSymmetricKey sqlClientSymmetricKey, out SqlEncryptionKeyInfo encryptionkeyInfoChosen, SqlConnection connection, SqlCommand command) { - Debug.Assert(sqlTceCipherInfoEntry != null, "sqlTceCipherInfoEntry should not be null in DecryptSymmetricKey."); - Debug.Assert(sqlTceCipherInfoEntry.ColumnEncryptionKeyValues != null, + Debug.Assert(sqlTceCipherInfoEntry is not null, "sqlTceCipherInfoEntry should not be null in DecryptSymmetricKey."); + Debug.Assert(sqlTceCipherInfoEntry.ColumnEncryptionKeyValues is not null, "sqlTceCipherInfoEntry.ColumnEncryptionKeyValues should not be null in DecryptSymmetricKey."); sqlClientSymmetricKey = null; encryptionkeyInfoChosen = null; Exception lastException = null; - SqlSymmetricKeyCache cache = SqlSymmetricKeyCache.GetInstance(); + SqlSymmetricKeyCache globalCekCache = SqlSymmetricKeyCache.GetInstance(); foreach (SqlEncryptionKeyInfo keyInfo in sqlTceCipherInfoEntry.ColumnEncryptionKeyValues) { try { - if (cache.GetKey(keyInfo, out sqlClientSymmetricKey, connection)) - { - encryptionkeyInfoChosen = keyInfo; - break; - } + sqlClientSymmetricKey = InstanceLevelProvidersAreRegistered(connection, command) ? + GetKeyFromLocalProviders(keyInfo, connection, command) : + globalCekCache.GetKey(keyInfo, connection, command); + encryptionkeyInfoChosen = keyInfo; + break; } catch (Exception e) { @@ -286,13 +286,45 @@ internal static void DecryptSymmetricKey(SqlTceCipherInfoEntry sqlTceCipherInfoE } } - if (null == sqlClientSymmetricKey) + if (sqlClientSymmetricKey is null) { - Debug.Assert(null != lastException, "CEK decryption failed without raising exceptions"); + Debug.Assert(lastException is not null, "CEK decryption failed without raising exceptions"); throw lastException; } - Debug.Assert(encryptionkeyInfoChosen != null, "encryptionkeyInfoChosen must have a value."); + Debug.Assert(encryptionkeyInfoChosen is not null, "encryptionkeyInfoChosen must have a value."); + } + + private static SqlClientSymmetricKey GetKeyFromLocalProviders(SqlEncryptionKeyInfo keyInfo, SqlConnection connection, SqlCommand command) + { + string serverName = connection.DataSource; + Debug.Assert(serverName is not null, @"serverName should not be null."); + + Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths is not null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); + + ThrowIfKeyPathIsNotTrustedForServer(serverName, keyInfo.keyPath); + if (!TryGetColumnEncryptionKeyStoreProvider(keyInfo.keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider, connection, command)) + { + throw SQL.UnrecognizedKeyStoreProviderName(keyInfo.keyStoreName, + SqlConnection.GetColumnEncryptionSystemKeyStoreProvidersNames(), + GetListOfProviderNamesThatWereSearched(connection, command)); + } + + // Decrypt the CEK + // We will simply bubble up the exception from the DecryptColumnEncryptionKey function. + byte[] plaintextKey; + try + { + plaintextKey = provider.DecryptColumnEncryptionKey(keyInfo.keyPath, keyInfo.algorithmName, keyInfo.encryptedKey); + } + catch (Exception e) + { + // Generate a new exception and throw. + string keyHex = GetBytesAsString(keyInfo.encryptedKey, fLast: true, countOfBytes: 10); + throw SQL.KeyDecryptionFailed(keyInfo.keyStoreName, keyHex, e); + } + + return new SqlClientSymmetricKey(plaintextKey); } /// @@ -311,62 +343,50 @@ internal static int GetBase64LengthFromByteLength(int byteLength) /// /// Verifies Column Master Key Signature. /// - internal static void VerifyColumnMasterKeySignature(string keyStoreName, string keyPath, bool isEnclaveEnabled, byte[] CMKSignature, SqlConnection connection) + internal static void VerifyColumnMasterKeySignature(string keyStoreName, string keyPath, bool isEnclaveEnabled, byte[] CMKSignature, SqlConnection connection, SqlCommand command) { bool isValidSignature = false; try { - Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths != null, + Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths is not null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); - if (CMKSignature == null || CMKSignature.Length == 0) + if (CMKSignature is null || CMKSignature.Length == 0) { throw SQL.ColumnMasterKeySignatureNotFound(keyPath); } - // Check against the trusted key paths - // - // Get the List corresponding to the connected server - IList trustedKeyPaths; - string serverName = connection.DataSource; - if (SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.TryGetValue(serverName, out trustedKeyPaths)) - { - // If the list is null or is empty or if the keyPath doesn't exist in the trusted key paths, then throw an exception. - if ((trustedKeyPaths == null) || (trustedKeyPaths.Count() == 0) || - // (trustedKeyPaths.Where(s => s.Equals(keyInfo.keyPath, StringComparison.InvariantCultureIgnoreCase)).Count() == 0)) { - (trustedKeyPaths.Any( - s => s.Equals(keyPath, StringComparison.InvariantCultureIgnoreCase)) == false)) - { - // throw an exception since the key path is not in the trusted key paths list for this server - throw SQL.UntrustedKeyPath(keyPath, serverName); - } - } + ThrowIfKeyPathIsNotTrustedForServer(connection.DataSource, keyPath); - // Key Not found, attempt to look up the provider and verify CMK Signature - SqlColumnEncryptionKeyStoreProvider provider; - if (!SqlConnection.TryGetColumnEncryptionKeyStoreProvider(keyStoreName, out provider, connection)) + // Attempt to look up the provider and verify CMK Signature + if (!TryGetColumnEncryptionKeyStoreProvider(keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider, connection, command)) { throw SQL.InvalidKeyStoreProviderName(keyStoreName, - SqlConnection.GetColumnEncryptionSystemKeyStoreProviders(), - SqlConnection.GetColumnEncryptionCustomKeyStoreProviders(connection)); + SqlConnection.GetColumnEncryptionSystemKeyStoreProvidersNames(), + GetListOfProviderNamesThatWereSearched(connection, command)); } - bool? signatureVerificationResult = ColumnMasterKeyMetadataSignatureVerificationCache.GetSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature); - - if (signatureVerificationResult == null) + if (InstanceLevelProvidersAreRegistered(connection, command)) { - // We will simply bubble up the exception from VerifyColumnMasterKeyMetadata function. - isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, - CMKSignature); - - ColumnMasterKeyMetadataSignatureVerificationCache.AddSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature, isValidSignature); + isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, CMKSignature); } else { - isValidSignature = signatureVerificationResult.Value; - } + bool? signatureVerificationResult = ColumnMasterKeyMetadataSignatureVerificationCache.GetSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature); + if (signatureVerificationResult is null) + { + // We will simply bubble up the exception from VerifyColumnMasterKeyMetadata function. + isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, + CMKSignature); + ColumnMasterKeyMetadataSignatureVerificationCache.AddSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature, isValidSignature); + } + else + { + isValidSignature = signatureVerificationResult.Value; + } + } } catch (Exception e) { @@ -378,5 +398,48 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string throw SQL.ColumnMasterKeySignatureVerificationFailed(keyPath); } } + + private static bool InstanceLevelProvidersAreRegistered(SqlConnection connection, SqlCommand command) => + connection.HasColumnEncryptionKeyStoreProvidersRegistered || + (command is not null && command.HasColumnEncryptionKeyStoreProvidersRegistered); + + internal static void ThrowIfKeyPathIsNotTrustedForServer(string serverName, string keyPath) + { + // Check against the trusted key paths + // Get the List corresponding to the connected server + if (SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.TryGetValue(serverName, out IList trustedKeyPaths)) + { + // If the list is null or is empty or if the keyPath doesn't exist in the trusted key paths, then throw an exception. + if (trustedKeyPaths is null || trustedKeyPaths.Count() == 0 || + trustedKeyPaths.Any(trustedKeyPath => trustedKeyPath.Equals(keyPath, StringComparison.InvariantCultureIgnoreCase)) == false) + { + // throw an exception since the key path is not in the trusted key paths list for this server + throw SQL.UntrustedKeyPath(keyPath, serverName); + } + } + } + + internal static bool TryGetColumnEncryptionKeyStoreProvider(string keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider, SqlConnection connection, SqlCommand command) + { + Debug.Assert(!string.IsNullOrWhiteSpace(keyStoreName), "Provider name is invalid"); + + // command may be null because some callers do not have a command object, eg SqlBulkCopy + if (command is not null && command.HasColumnEncryptionKeyStoreProvidersRegistered) + { + return command.TryGetColumnEncryptionKeyStoreProvider(keyStoreName, out provider); + } + + return connection.TryGetColumnEncryptionKeyStoreProvider(keyStoreName, out provider); + } + + internal static List GetListOfProviderNamesThatWereSearched(SqlConnection connection, SqlCommand command) + { + if (command is not null && command.HasColumnEncryptionKeyStoreProvidersRegistered) + { + return command.GetColumnEncryptionCustomKeyStoreProvidersNames(); + } + + return connection.GetColumnEncryptionCustomKeyStoreProvidersNames(); + } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs index 6947bfa1e4..0b2afa2a1c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs @@ -33,10 +33,10 @@ internal static SqlSymmetricKeyCache GetInstance() /// /// Retrieves Symmetric Key (in plaintext) given the encryption material. /// - internal bool GetKey(SqlEncryptionKeyInfo keyInfo, out SqlClientSymmetricKey encryptionKey, SqlConnection connection) + internal SqlClientSymmetricKey GetKey(SqlEncryptionKeyInfo keyInfo, SqlConnection connection, SqlCommand command) { string serverName = connection.DataSource; - Debug.Assert(serverName != null, @"serverName should not be null."); + Debug.Assert(serverName is not null, @"serverName should not be null."); StringBuilder cacheLookupKeyBuilder = new StringBuilder(serverName, capacity: serverName.Length + SqlSecurityUtility.GetBase64LengthFromByteLength(keyInfo.encryptedKey.Length) + keyInfo.keyStoreName.Length + 2/*separators*/); #if DEBUG @@ -55,35 +55,18 @@ internal bool GetKey(SqlEncryptionKeyInfo keyInfo, out SqlClientSymmetricKey enc #endif //DEBUG // Lookup the key in cache - encryptionKey = _cache.Get(cacheLookupKey) as SqlClientSymmetricKey; - - if (encryptionKey == null) + if (!(_cache.Get(cacheLookupKey) is SqlClientSymmetricKey encryptionKey)) { - Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths != null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); + Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths is not null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); - // Check against the trusted key paths - // - // Get the List corresponding to the connected server - IList trustedKeyPaths; - if (SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.TryGetValue(serverName, out trustedKeyPaths)) - { - // If the list is null or is empty or if the keyPath doesn't exist in the trusted key paths, then throw an exception. - if ((trustedKeyPaths == null) || (trustedKeyPaths.Count() == 0) || - // (trustedKeyPaths.Where(s => s.Equals(keyInfo.keyPath, StringComparison.InvariantCultureIgnoreCase)).Count() == 0)) { - (trustedKeyPaths.Any(s => s.Equals(keyInfo.keyPath, StringComparison.InvariantCultureIgnoreCase)) == false)) - { - // throw an exception since the key path is not in the trusted key paths list for this server - throw SQL.UntrustedKeyPath(keyInfo.keyPath, serverName); - } - } + SqlSecurityUtility.ThrowIfKeyPathIsNotTrustedForServer(serverName, keyInfo.keyPath); // Key Not found, attempt to look up the provider and decrypt CEK - SqlColumnEncryptionKeyStoreProvider provider; - if (!SqlConnection.TryGetColumnEncryptionKeyStoreProvider(keyInfo.keyStoreName, out provider, connection)) + if (!SqlSecurityUtility.TryGetColumnEncryptionKeyStoreProvider(keyInfo.keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider, connection, command)) { throw SQL.UnrecognizedKeyStoreProviderName(keyInfo.keyStoreName, - SqlConnection.GetColumnEncryptionSystemKeyStoreProviders(), - SqlConnection.GetColumnEncryptionCustomKeyStoreProviders(connection)); + SqlConnection.GetColumnEncryptionSystemKeyStoreProvidersNames(), + SqlSecurityUtility.GetListOfProviderNamesThatWereSearched(connection, command)); } // Decrypt the CEK @@ -112,7 +95,7 @@ internal bool GetKey(SqlEncryptionKeyInfo keyInfo, out SqlClientSymmetricKey enc } } - return true; + return encryptionKey; } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs index d703d4f748..dfec766011 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs @@ -11,7 +11,27 @@ namespace Microsoft.Data.SqlClient.Tests.AlwaysEncryptedTests { public class ExceptionRegisterKeyStoreProvider { - private SqlConnection connection = new SqlConnection(); + private SqlConnection connection = new(); + private SqlCommand command = new(); + + private const string dummyProviderName1 = "DummyProvider1"; + private const string dummyProviderName2 = "DummyProvider2"; + private const string dummyProviderName3 = "DummyProvider3"; + private const string dummyProviderName4 = "DummyProvider4"; + + private IDictionary singleKeyStoreProvider = + new Dictionary() + { + {dummyProviderName1, new DummyKeyStoreProvider() } + }; + + private IDictionary multipleKeyStoreProviders = + new Dictionary() + { + { dummyProviderName2, new DummyKeyStoreProvider() }, + { dummyProviderName3, new DummyKeyStoreProvider() }, + { dummyProviderName4, new DummyKeyStoreProvider() } + }; [Fact] public void TestNullDictionary() @@ -20,11 +40,7 @@ public void TestNullDictionary() string expectedMessage = SystemDataResourceManager.Instance.TCE_NullCustomKeyStoreProviderDictionary; IDictionary customProviders = null; - ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - Assert.Contains(expectedMessage, e.Message); - - e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); - Assert.Contains(expectedMessage, e.Message); + AssertExceptionIsThrownForAllCustomProviderCaches(customProviders, expectedMessage); } [Fact] @@ -32,15 +48,13 @@ public void TestInvalidProviderName() { // Verify the namespace reservation string providerWithReservedSystemPrefix = "MSSQL_DUMMY"; - string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_InvalidCustomKeyStoreProviderName, providerWithReservedSystemPrefix, "MSSQL_"); - IDictionary customProviders = new Dictionary(); + string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_InvalidCustomKeyStoreProviderName, + providerWithReservedSystemPrefix, "MSSQL_"); + IDictionary customProviders = + new Dictionary(); customProviders.Add(providerWithReservedSystemPrefix, new DummyKeyStoreProvider()); - ArgumentException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - Assert.Contains(expectedMessage, e.Message); - - e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); - Assert.Contains(expectedMessage, e.Message); + AssertExceptionIsThrownForAllCustomProviderCaches(customProviders, expectedMessage); } [Fact] @@ -49,14 +63,11 @@ public void TestNullProviderValue() // Verify null provider value are not supported string providerName = "DUMMY"; string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_NullProviderValue, providerName); - IDictionary customProviders = new Dictionary(); + IDictionary customProviders = + new Dictionary(); customProviders.Add(providerName, null); - ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - Assert.Contains(expectedMessage, e.Message); - - e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); - Assert.Contains(expectedMessage, e.Message); + AssertExceptionIsThrownForAllCustomProviderCaches(customProviders, expectedMessage); } [Fact] @@ -64,14 +75,11 @@ public void TestEmptyProviderName() { // Verify Empty provider names are not supported. string expectedMessage = SystemDataResourceManager.Instance.TCE_EmptyProviderName; - IDictionary customProviders = new Dictionary(); + IDictionary customProviders = + new Dictionary(); customProviders.Add(" ", new DummyKeyStoreProvider()); - ArgumentNullException e = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - Assert.Contains(expectedMessage, e.Message); - - e = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); - Assert.Contains(expectedMessage, e.Message); + AssertExceptionIsThrownForAllCustomProviderCaches(customProviders, expectedMessage); } [Fact] @@ -95,44 +103,57 @@ public void TestCanSetGlobalProvidersOnlyOnce() } [Fact] - public void TestCanSetInstanceProvidersMoreThanOnce() + public void TestCanSetConnectionInstanceProvidersMoreThanOnce() { - const string dummyProviderName1 = "DummyProvider1"; - const string dummyProviderName2 = "DummyProvider2"; - const string dummyProviderName3 = "DummyProvider3"; - IDictionary singleKeyStoreProvider = - new Dictionary() - { - {dummyProviderName1, new DummyKeyStoreProvider() } - }; + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(singleKeyStoreProvider); + IReadOnlyDictionary providerCache = GetProviderCacheFrom(connection); + AssertProviderCacheContainsExpectedProviders(providerCache, singleKeyStoreProvider); - IDictionary multipleKeyStoreProviders = - new Dictionary() - { - { dummyProviderName2, new DummyKeyStoreProvider() }, - { dummyProviderName3, new DummyKeyStoreProvider() } - }; + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(multipleKeyStoreProviders); + providerCache = GetProviderCacheFrom(connection); + AssertProviderCacheContainsExpectedProviders(providerCache, multipleKeyStoreProviders); + } - using (SqlConnection connection = new SqlConnection()) - { - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(singleKeyStoreProvider); - IReadOnlyDictionary instanceCache = - GetInstanceCacheFromConnection(connection); - Assert.Single(instanceCache); - Assert.True(instanceCache.ContainsKey(dummyProviderName1)); - - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(multipleKeyStoreProviders); - instanceCache = GetInstanceCacheFromConnection(connection); - Assert.Equal(2, instanceCache.Count); - Assert.True(instanceCache.ContainsKey(dummyProviderName2)); - Assert.True(instanceCache.ContainsKey(dummyProviderName3)); - } + [Fact] + public void TestCanSetCommandInstanceProvidersMoreThanOnce() + { + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(singleKeyStoreProvider); + IReadOnlyDictionary providerCache = GetProviderCacheFrom(command); + AssertProviderCacheContainsExpectedProviders(providerCache, singleKeyStoreProvider); - IReadOnlyDictionary GetInstanceCacheFromConnection(SqlConnection conn) + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(multipleKeyStoreProviders); + providerCache = GetProviderCacheFrom(command); + AssertProviderCacheContainsExpectedProviders(providerCache, multipleKeyStoreProviders); + } + + private void AssertExceptionIsThrownForAllCustomProviderCaches(IDictionary customProviders, string expectedMessage) + where T : Exception + { + Exception ex = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); + Assert.Contains(expectedMessage, ex.Message); + + ex = Assert.Throws(() => connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customProviders)); + Assert.Contains(expectedMessage, ex.Message); + + ex = Assert.Throws(() => command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProviders)); + Assert.Contains(expectedMessage, ex.Message); + } + + private IReadOnlyDictionary GetProviderCacheFrom(object obj) + { + FieldInfo instanceCacheField = obj.GetType().GetField( + "_customColumnEncryptionKeyStoreProviders", BindingFlags.NonPublic | BindingFlags.Instance); + return instanceCacheField.GetValue(obj) as IReadOnlyDictionary; + } + + private void AssertProviderCacheContainsExpectedProviders( + IReadOnlyDictionary providerCache, + IDictionary expectedProviders) + { + Assert.Equal(expectedProviders.Count, providerCache.Count); + foreach (string key in expectedProviders.Keys) { - FieldInfo instanceCacheField = conn.GetType().GetField( - "_customColumnEncryptionKeyStoreProviders", BindingFlags.NonPublic | BindingFlags.Instance); - return instanceCacheField.GetValue(conn) as IReadOnlyDictionary; + Assert.True(providerCache.ContainsKey(key)); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs index c556378a6d..07e9e93146 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs @@ -311,12 +311,12 @@ internal static Object GetSqlCipherMetadata(ushort ordinal, byte cipherAlgorithm internal static byte[] DecryptWithKey(byte[] cipherText, Object cipherMd) { - return (byte[])SqlSecurityUtilDecryptWithKey.Invoke(null, new Object[] { cipherText, cipherMd, new SqlConnection() }); + return (byte[])SqlSecurityUtilDecryptWithKey.Invoke(null, new Object[] { cipherText, cipherMd, new SqlConnection(), new SqlCommand() }); } internal static byte[] EncryptWithKey(byte[] plainText, Object cipherMd) { - return (byte[])SqlSecurityUtilEncryptWithKey.Invoke(null, new Object[] { plainText, cipherMd, new SqlConnection() }); + return (byte[])SqlSecurityUtilEncryptWithKey.Invoke(null, new Object[] { plainText, cipherMd, new SqlConnection(), new SqlCommand() }); } /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVTests.cs index 85e81cfd99..0f7aa8aa20 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVTests.cs @@ -3,7 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Linq; +using Azure.Identity; +using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; using Xunit; @@ -89,6 +92,51 @@ public void TestRoundTripWithAKVAndCertStoreProvider() } } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAKVSetupAvailable))] + public void TestLocalCekCacheIsScopedToProvider() + { + using (SqlConnection sqlConnection = new(string.Concat(DataTestUtility.TCPConnectionString, @";Column Encryption Setting = Enabled;"))) + { + sqlConnection.Open(); + + Customer customer = new(45, "Microsoft", "Corporation"); + + // Test INPUT parameter on an encrypted parameter + using (SqlCommand sqlCommand = new($"SELECT CustomerId, FirstName, LastName FROM [{akvTableName}] WHERE FirstName = @firstName", + sqlConnection)) + { + SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); + customerFirstParam.Direction = System.Data.ParameterDirection.Input; + customerFirstParam.ForceColumnEncryption = true; + + SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(); + sqlDataReader.Close(); + + SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider = + new(new SqlClientCustomTokenCredential()); + + Dictionary customProvider = new() + { + { SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, sqlColumnEncryptionAzureKeyVaultProvider } + }; + + // execute a query using provider from command-level cache. this will cache the cek in the local cek cache + sqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProvider); + SqlDataReader sqlDataReader2 = sqlCommand.ExecuteReader(); + sqlDataReader2.Close(); + + // global cek cache and local cek cache are populated above + // when using a new per-command provider, it will only use its local cek cache + // the following query should fail due to an empty cek cache and invalid credentials + customProvider[SqlColumnEncryptionAzureKeyVaultProvider.ProviderName] = + new SqlColumnEncryptionAzureKeyVaultProvider(new ClientSecretCredential("tenant", "client", "secret")); + sqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProvider); + Exception ex = Assert.Throws(() => sqlCommand.ExecuteReader()); + Assert.Contains("ClientSecretCredential authentication failed", ex.Message); + } + } + } + /// /// Validates that the results are the ones expected. /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs index 2fc97e46d6..6097c8a662 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs @@ -6,9 +6,11 @@ using Azure.Identity; using Xunit; using Azure.Security.KeyVault.Keys; -using Azure.Core; using System.Reflection; using System; +using System.Linq; +using System.Collections.Generic; +using System.Threading; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { @@ -16,6 +18,8 @@ public static class AKVUnitTests { const string EncryptionAlgorithm = "RSA_OAEP"; public static readonly byte[] s_columnEncryptionKey = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }; + private const string cekCacheName = "_columnEncryptionKeyCache"; + private const string signatureVerificationResultCacheName = "_columnMasterKeyMetadataSignatureVerificationCache"; [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] public static void LegacyAuthenticationCallbackTest() @@ -79,7 +83,7 @@ public static void ReturnSpecifiedVersionOfKeyWhenItIsNotTheMostRecentVersion() SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential); // Perform an operation to initialize the internal caches azureKeyProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVOriginalUrl, EncryptionAlgorithm, s_columnEncryptionKey); - + PropertyInfo keyCryptographerProperty = azureKeyProvider.GetType().GetProperty("KeyCryptographer", BindingFlags.NonPublic | BindingFlags.Instance); var keyCryptographer = keyCryptographerProperty.GetValue(azureKeyProvider); MethodInfo getKeyMethod = keyCryptographer.GetType().GetMethod("GetKey", BindingFlags.NonPublic | BindingFlags.Instance); @@ -96,12 +100,136 @@ public static void ReturnSpecifiedVersionOfKeyWhenItIsNotTheMostRecentVersion() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] public static void ThrowWhenUrlHasLessThanThreeSegments() { - SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential()); + SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new(new SqlClientCustomTokenCredential()); string invalidKeyPath = "https://my-key-vault.vault.azure.net/keys"; Exception ex1 = Assert.Throws(() => azureKeyProvider.EncryptColumnEncryptionKey(invalidKeyPath, EncryptionAlgorithm, s_columnEncryptionKey)); Assert.Contains($"Invalid url specified: '{invalidKeyPath}'", ex1.Message); Exception ex2 = Assert.Throws(() => azureKeyProvider.DecryptColumnEncryptionKey(invalidKeyPath, EncryptionAlgorithm, s_columnEncryptionKey)); Assert.Contains($"Invalid url specified: '{invalidKeyPath}'", ex2.Message); } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] + public static void DecryptedCekIsCachedDuringDecryption() + { + SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new(new SqlClientCustomTokenCredential()); + byte[] plaintextKey1 = { 1, 2, 3 }; + byte[] plaintextKey2 = { 1, 2, 3 }; + byte[] plaintextKey3 = { 0, 1, 2, 3 }; + byte[] encryptedKey1 = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", plaintextKey1); + byte[] encryptedKey2 = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", plaintextKey2); + byte[] encryptedKey3 = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", plaintextKey3); + + byte[] decryptedKey1 = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey1); + Assert.Equal(1, GetCacheCount(cekCacheName, akvProvider)); + Assert.Equal(plaintextKey1, decryptedKey1); + + decryptedKey1 = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey1); + Assert.Equal(1, GetCacheCount(cekCacheName, akvProvider)); + Assert.Equal(plaintextKey1, decryptedKey1); + + byte[] decryptedKey2 = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey2); + Assert.Equal(2, GetCacheCount(cekCacheName, akvProvider)); + Assert.Equal(plaintextKey2, decryptedKey2); + + byte[] decryptedKey3 = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey3); + Assert.Equal(3, GetCacheCount(cekCacheName, akvProvider)); + Assert.Equal(plaintextKey3, decryptedKey3); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] + public static void SignatureVerificationResultIsCachedDuringVerification() + { + SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new(new SqlClientCustomTokenCredential()); + byte[] signature = akvProvider.SignColumnMasterKeyMetadata(DataTestUtility.AKVUrl, true); + byte[] signature2 = akvProvider.SignColumnMasterKeyMetadata(DataTestUtility.AKVUrl, true); + byte[] signatureWithoutEnclave = akvProvider.SignColumnMasterKeyMetadata(DataTestUtility.AKVUrl, false); + + Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, true, signature)); + Assert.Equal(1, GetCacheCount(signatureVerificationResultCacheName, akvProvider)); + + Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, true, signature)); + Assert.Equal(1, GetCacheCount(signatureVerificationResultCacheName, akvProvider)); + + Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, true, signature2)); + Assert.Equal(1, GetCacheCount(signatureVerificationResultCacheName, akvProvider)); + + Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, false, signatureWithoutEnclave)); + Assert.Equal(2, GetCacheCount(signatureVerificationResultCacheName, akvProvider)); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] + public static void CekCacheEntryIsEvictedAfterTtlExpires() + { + SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new(new SqlClientCustomTokenCredential()); + akvProvider.ColumnEncryptionKeyCacheTtl = TimeSpan.FromSeconds(5); + byte[] plaintextKey = { 1, 2, 3 }; + byte[] encryptedKey = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", plaintextKey); + + akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey); + Assert.True(CekCacheContainsKey(encryptedKey, akvProvider)); + Assert.Equal(1, GetCacheCount(cekCacheName, akvProvider)); + + Thread.Sleep(TimeSpan.FromSeconds(5)); + Assert.False(CekCacheContainsKey(encryptedKey, akvProvider)); + Assert.Equal(0, GetCacheCount(cekCacheName, akvProvider)); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] + public static void CekCacheShouldBeDisabledWhenAkvProviderIsRegisteredGlobally() + { + if (SQLSetupStrategyAzureKeyVault.IsAKVProviderRegistered) + { + SqlConnection conn = new(); + FieldInfo globalCacheField = conn.GetType().GetField( + "s_globalCustomColumnEncryptionKeyStoreProviders", BindingFlags.Static | BindingFlags.NonPublic); + IReadOnlyDictionary globalProviders = + globalCacheField.GetValue(conn) as IReadOnlyDictionary; + + SqlColumnEncryptionAzureKeyVaultProvider akvProviderInGlobalCache = + globalProviders["AZURE_KEY_VAULT"] as SqlColumnEncryptionAzureKeyVaultProvider; + byte[] plaintextKey = { 1, 2, 3 }; + byte[] encryptedKey = akvProviderInGlobalCache.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", plaintextKey); + + akvProviderInGlobalCache.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey); + Assert.Equal(0, GetCacheCount(cekCacheName, akvProviderInGlobalCache)); + } + } + + private static int GetCacheCount(string cacheName, SqlColumnEncryptionAzureKeyVaultProvider akvProvider) + { + var cacheInstance = GetCacheInstance(cacheName, akvProvider); + Type cacheType = cacheInstance.GetType(); + PropertyInfo countProperty = cacheType.GetProperty("Count", BindingFlags.Instance | BindingFlags.NonPublic); + int countValue = (int)countProperty.GetValue(cacheInstance); + return countValue; + } + + private static bool CekCacheContainsKey(byte[] encryptedCek, SqlColumnEncryptionAzureKeyVaultProvider akvProvider) + { + var cacheInstance = GetCacheInstance("_columnEncryptionKeyCache", akvProvider); + Type cacheType = cacheInstance.GetType(); + MethodInfo containsMethod = cacheType.GetMethod("Contains", BindingFlags.Instance | BindingFlags.NonPublic); + bool containsResult = (bool)containsMethod.Invoke(cacheInstance, new object[] { ToHexString(encryptedCek) }); + return containsResult; + } + + private static object GetCacheInstance(string cacheName, SqlColumnEncryptionAzureKeyVaultProvider akvProvider) + { + Assembly akvProviderAssembly = typeof(SqlColumnEncryptionAzureKeyVaultProvider).Assembly; + Type akvProviderType = akvProviderAssembly.GetType( + "Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.SqlColumnEncryptionAzureKeyVaultProvider"); + FieldInfo cacheField = akvProviderType.GetField(cacheName, BindingFlags.Instance | BindingFlags.NonPublic); + return cacheField.GetValue(akvProvider); + } + + private static string ToHexString(byte[] source) + { + if (source is null) + { + return null; + } + + return "0x" + BitConverter.ToString(source).Replace("-", ""); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 30e24f8960..8f18312f94 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -26,10 +26,34 @@ public sealed class ApiShould : IClassFixture, IDis private readonly string _tableName; + private Dictionary _requiredProvider = new() + { + { DummyKeyStoreProvider.Name, new DummyKeyStoreProvider() } + }; + + private const string NotRequiredProviderName = "DummyProvider2"; + private Dictionary _notRequiredProvider = new() + { + { NotRequiredProviderName, new DummyKeyStoreProvider() } + }; + + private string _failedToDecryptMessage; + private string _providerNotFoundMessage = string.Format( + SystemDataResourceManager.Instance.TCE_UnrecognizedKeyStoreProviderName, + DummyKeyStoreProvider.Name, + "'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'", + $"'{NotRequiredProviderName}'"); + public ApiShould(PlatformSpecificTestContext context) { _fixture = context.Fixture; _tableName = _fixture.ApiTestTable.Name; + + ApiTestTable _customKeyStoreProviderTable = _fixture.CustomKeyStoreProviderTestTable as ApiTestTable; + byte[] encryptedCek = _customKeyStoreProviderTable.columnEncryptionKey1.EncryptedValue; + string _lastTenBytesCek = BitConverter.ToString(encryptedCek, encryptedCek.Length - 10, 10); + _failedToDecryptMessage = string.Format(SystemDataResourceManager.Instance.TCE_KeyDecryptionFailed, + DummyKeyStoreProvider.Name, _lastTenBytesCek); } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] @@ -2117,40 +2141,16 @@ public void TestSqlCommandCancellationToken(string connection, int initalValue, [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] [ClassData(typeof(AEConnectionStringProvider))] - public void TestCustomKeyStoreProviderDuringAeQuery(string connectionString) + public void TestConnectionCustomKeyStoreProviderDuringAeQuery(string connectionString) { if (!SQLSetupStrategyAzureKeyVault.IsAKVProviderRegistered) { SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider = - new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential()); + new(new SqlClientCustomTokenCredential()); SQLSetupStrategyAzureKeyVault.RegisterGlobalProviders(sqlColumnEncryptionAzureKeyVaultProvider); } - Dictionary requiredProvider = - new Dictionary() - { - { DummyKeyStoreProvider.Name, new DummyKeyStoreProvider() } - }; - - string notRequiredProviderName = "DummyProvider2"; - Dictionary notRequiredProvider = - new Dictionary() - { - { notRequiredProviderName, new DummyKeyStoreProvider() } - }; - - ApiTestTable customKeyStoreProviderTable = _fixture.CustomKeyStoreProviderTestTable as ApiTestTable; - byte[] encryptedCek = customKeyStoreProviderTable.columnEncryptionKey1.EncryptedValue; - string lastTenBytesCek = BitConverter.ToString(encryptedCek, encryptedCek.Length - 10, 10); - - string failedToDecryptMessage = string.Format(SystemDataResourceManager.Instance.TCE_KeyDecryptionFailed, - DummyKeyStoreProvider.Name, lastTenBytesCek); - string providerNotFoundMessage = string.Format(SystemDataResourceManager.Instance.TCE_UnrecognizedKeyStoreProviderName, - DummyKeyStoreProvider.Name, - "'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'", - $"'{notRequiredProviderName}'"); - - using (SqlConnection connection = new SqlConnection(connectionString)) + using (SqlConnection connection = new(connectionString)) { connection.Open(); @@ -2158,45 +2158,113 @@ public void TestCustomKeyStoreProviderDuringAeQuery(string connectionString) // provider will be found but it will throw when its methods are called Exception ex = Assert.Throws( () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); - Assert.Contains(failedToDecryptMessage, ex.Message); - Assert.True(ex.InnerException is NotImplementedException); + AssertExceptionCausedByFailureToDecrypt(ex); // not required provider in instance cache // it should not fall back to the global cache so the right provider will not be found - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(notRequiredProvider); + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(_notRequiredProvider); ex = Assert.Throws( () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); - Assert.Equal(providerNotFoundMessage, ex.Message); + Assert.Equal(_providerNotFoundMessage, ex.Message); // required provider in instance cache // if the instance cache is not empty, it is always checked for the provider. // => if the provider is found, it must have been retrieved from the instance cache and not the global cache - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(requiredProvider); + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(_requiredProvider); ex = Assert.Throws( () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); - Assert.Contains(failedToDecryptMessage, ex.Message); - Assert.True(ex.InnerException is NotImplementedException); + AssertExceptionCausedByFailureToDecrypt(ex); // not required provider will replace the previous entry so required provider will not be found - connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(notRequiredProvider); + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(_notRequiredProvider); ex = Assert.Throws( () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); - Assert.Equal(providerNotFoundMessage, ex.Message); + Assert.Equal(_providerNotFoundMessage, ex.Message); } - void ExecuteQueryThatRequiresCustomKeyStoreProvider(SqlConnection connection) + using (SqlConnection connection = new(connectionString)) { - using (SqlCommand command = new SqlCommand( - null, connection, null, SqlCommandColumnEncryptionSetting.Enabled)) + connection.Open(); + + // new connection instance should have an empty cache and query will fall back to global cache + // which contains the required provider + Exception ex = Assert.Throws( + () => ExecuteQueryThatRequiresCustomKeyStoreProvider(connection)); + AssertExceptionCausedByFailureToDecrypt(ex); + } + } + + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestCommandCustomKeyStoreProviderDuringAeQuery(string connectionString) + { + if (!SQLSetupStrategyAzureKeyVault.IsAKVProviderRegistered) + { + SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider = + new(new SqlClientCustomTokenCredential()); + SQLSetupStrategyAzureKeyVault.RegisterGlobalProviders(sqlColumnEncryptionAzureKeyVaultProvider); + } + + using (SqlConnection connection = new(connectionString)) + { + connection.Open(); + using (SqlCommand command = CreateCommandThatRequiresCustomKeyStoreProvider(connection)) { - command.CommandText = - $"SELECT * FROM [{_fixture.CustomKeyStoreProviderTestTable.Name}] WHERE CustomerID = @id"; - command.Parameters.AddWithValue(@"id", 9); - command.ExecuteReader(); + // will use DummyProvider in global cache + // provider will be found but it will throw when its methods are called + Exception ex = Assert.Throws(() => command.ExecuteReader()); + AssertExceptionCausedByFailureToDecrypt(ex); + + // required provider will be found in command cache + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(_requiredProvider); + ex = Assert.Throws(() => command.ExecuteReader()); + AssertExceptionCausedByFailureToDecrypt(ex); + + // not required provider in command cache + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(_notRequiredProvider); + ex = Assert.Throws(() => command.ExecuteReader()); + Assert.Equal(_providerNotFoundMessage, ex.Message); + + // not required provider in command cache, required provider in connection cache + // should not fall back to connection cache or global cache + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(_requiredProvider); + ex = Assert.Throws(() => command.ExecuteReader()); + Assert.Equal(_providerNotFoundMessage, ex.Message); + + using (SqlCommand command2 = CreateCommandThatRequiresCustomKeyStoreProvider(connection)) + { + // new command instance should have an empty cache and query will fall back to connection cache + // which contains the required provider + ex = Assert.Throws(() => command2.ExecuteReader()); + AssertExceptionCausedByFailureToDecrypt(ex); + } } } } + private void ExecuteQueryThatRequiresCustomKeyStoreProvider(SqlConnection connection) + { + using (SqlCommand command = CreateCommandThatRequiresCustomKeyStoreProvider(connection)) + { + command.ExecuteReader(); + } + } + + private SqlCommand CreateCommandThatRequiresCustomKeyStoreProvider(SqlConnection connection) + { + SqlCommand command = new( + $"SELECT * FROM [{_fixture.CustomKeyStoreProviderTestTable.Name}] WHERE CustomerID = @id", + connection, null, SqlCommandColumnEncryptionSetting.Enabled); + command.Parameters.AddWithValue("id", 9); + return command; + } + + private void AssertExceptionCausedByFailureToDecrypt(Exception ex) + { + Assert.Contains(_failedToDecryptMessage, ex.Message); + Assert.True(ex.InnerException is NotImplementedException); + } + private SqlDataAdapter CreateSqlDataAdapter(SqlConnection sqlConnection) { // Create a SqlDataAdapter. diff --git a/tools/props/Versions.props b/tools/props/Versions.props index ec4365798c..db7f049b67 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -47,6 +47,7 @@ [1.6.0,2.0.0) [4.0.3,5.0.0) + 5.0.0 diff --git a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec index d063fad949..6b40910512 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -28,16 +28,19 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti + + + From 4316474cdb6f3e09d0a9f10d70042215fa1fa74c Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 17 May 2021 16:58:44 -0700 Subject: [PATCH 35/87] Feature | Introduce "Active Directory Default" authentication mode (#1043) --- .../SqlAuthenticationMethod.xml | 4 ++ .../netcore/ref/Microsoft.Data.SqlClient.cs | 2 + .../Data/Common/DbConnectionStringCommon.cs | 53 ++++++++++++++++-- ...uthenticationProviderManager.NetCoreApp.cs | 2 + .../SqlAuthenticationProviderManager.cs | 2 + .../Microsoft/Data/SqlClient/SqlConnection.cs | 33 ++++++++--- .../Data/SqlClient/SqlConnectionString.cs | 9 ++- .../SqlClient/SqlInternalConnectionTds.cs | 3 + .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 16 +++--- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 7 ++- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 3 + .../netcore/src/Resources/Strings.Designer.cs | 12 ++-- .../netcore/src/Resources/Strings.resx | 6 +- .../netfx/ref/Microsoft.Data.SqlClient.cs | 2 + .../Data/Common/DbConnectionStringCommon.cs | 54 ++++++++++++++++-- .../SqlAuthenticationProviderManager.cs | 4 ++ .../Microsoft/Data/SqlClient/SqlConnection.cs | 34 ++++++++--- .../Data/SqlClient/SqlConnectionString.cs | 9 ++- .../SqlClient/SqlInternalConnectionTds.cs | 6 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 16 +++--- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 5 ++ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 6 ++ .../netfx/src/Resources/Strings.Designer.cs | 12 ++-- .../netfx/src/Resources/Strings.de.resx | 6 +- .../netfx/src/Resources/Strings.es.resx | 6 +- .../netfx/src/Resources/Strings.fr.resx | 6 +- .../netfx/src/Resources/Strings.it.resx | 6 +- .../netfx/src/Resources/Strings.ja.resx | 6 +- .../netfx/src/Resources/Strings.ko.resx | 6 +- .../netfx/src/Resources/Strings.pt-BR.resx | 6 +- .../netfx/src/Resources/Strings.resx | 6 +- .../netfx/src/Resources/Strings.ru.resx | 6 +- .../netfx/src/Resources/Strings.zh-Hans.resx | 6 +- .../netfx/src/Resources/Strings.zh-Hant.resx | 6 +- .../ActiveDirectoryAuthenticationProvider.cs | 56 +++++++++++++++---- .../SqlConnectionStringBuilderTest.cs | 2 + .../ConnectivityTests/AADConnectionTest.cs | 46 +++++++++++++++ .../tests/NuGet.config | 8 +++ 38 files changed, 372 insertions(+), 106 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/NuGet.config diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml index 54b8f3d0a7..a52a2ec41a 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml @@ -41,5 +41,9 @@ Alias for "Active Directory Managed Identity" authentication method. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the "client ID" of the user identity. 8 + + The authentication method uses Active Directory Default. Use this mode to connect to a SQL Database using multiple non-interactive authentication methods tried sequentially to acquire an access token. This method does not fallback to the "Active Directory Interactive" authentication method. + 9 + diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 824705550b..ed3f0b3c73 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -99,6 +99,8 @@ public enum SqlAuthenticationMethod ActiveDirectoryManagedIdentity = 7, /// ActiveDirectoryMSI = 8, + /// + ActiveDirectoryDefault = 9, /// NotSpecified = 0, /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 3c22c4ecd8..239e7e2715 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -99,6 +99,7 @@ internal static string ConvertToString(object value) private const string ApplicationIntentReadWriteString = "ReadWrite"; private const string ApplicationIntentReadOnlyString = "ReadOnly"; + const string SqlPasswordString = "Sql Password"; const string ActiveDirectoryPasswordString = "Active Directory Password"; const string ActiveDirectoryIntegratedString = "Active Directory Integrated"; @@ -107,13 +108,48 @@ internal static string ConvertToString(object value) const string ActiveDirectoryDeviceCodeFlowString = "Active Directory Device Code Flow"; internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity"; internal const string ActiveDirectoryMSIString = "Active Directory MSI"; + internal const string ActiveDirectoryDefaultString = "Active Directory Default"; - internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) +#if DEBUG + private static string[] s_supportedAuthenticationModes = + { + "NotSpecified", + "SqlPassword", + "ActiveDirectoryPassword", + "ActiveDirectoryIntegrated", + "ActiveDirectoryInteractive", + "ActiveDirectoryServicePrincipal", + "ActiveDirectoryDeviceCodeFlow", + "ActiveDirectoryManagedIdentity", + "ActiveDirectoryMSI", + "ActiveDirectoryDefault" + }; + + private static bool IsValidAuthenticationMethodEnum() { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed"); + string[] names = Enum.GetNames(typeof(SqlAuthenticationMethod)); + int l = s_supportedAuthenticationModes.Length; + bool listValid; + if (listValid = names.Length == l) + { + for (int i = 0; i < l; i++) + { + if (s_supportedAuthenticationModes[i].CompareTo(names[i]) != 0) + { + listValid = false; + } + } + } + return listValid; + } +#endif + internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) + { +#if DEBUG + Debug.Assert(IsValidAuthenticationMethodEnum(), "SqlAuthenticationMethod enum has changed, update needed"); +#endif bool isSuccess = false; - if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlPasswordString) || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.SqlPassword, CultureInfo.InvariantCulture))) { @@ -162,6 +198,12 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent result = SqlAuthenticationMethod.ActiveDirectoryMSI; isSuccess = true; } + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryDefaultString) + || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryDefault, CultureInfo.InvariantCulture))) + { + result = SqlAuthenticationMethod.ActiveDirectoryDefault; + isSuccess = true; + } else { result = DbConnectionStringDefaults.Authentication; @@ -606,7 +648,7 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value) { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed"); + Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 10, "SqlAuthenticationMethod enum has changed, update needed"); return value == SqlAuthenticationMethod.SqlPassword || value == SqlAuthenticationMethod.ActiveDirectoryPassword || value == SqlAuthenticationMethod.ActiveDirectoryIntegrated @@ -615,6 +657,7 @@ internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod valu || value == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || value == SqlAuthenticationMethod.ActiveDirectoryMSI + || value == SqlAuthenticationMethod.ActiveDirectoryDefault || value == SqlAuthenticationMethod.NotSpecified; } @@ -640,6 +683,8 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value) return ActiveDirectoryManagedIdentityString; case SqlAuthenticationMethod.ActiveDirectoryMSI: return ActiveDirectoryMSIString; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + return ActiveDirectoryDefaultString; default: return null; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs index e253829b2c..b204c8df81 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs @@ -152,6 +152,8 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe return SqlAuthenticationMethod.ActiveDirectoryManagedIdentity; case ActiveDirectoryMSI: return SqlAuthenticationMethod.ActiveDirectoryMSI; + case ActiveDirectoryDefault: + return SqlAuthenticationMethod.ActiveDirectoryDefault; default: throw SQL.UnsupportedAuthentication(authentication); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index 1841f016d0..4c101d30df 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -21,6 +21,7 @@ internal partial class SqlAuthenticationProviderManager private const string ActiveDirectoryDeviceCodeFlow = "active directory device code flow"; private const string ActiveDirectoryManagedIdentity = "active directory managed identity"; private const string ActiveDirectoryMSI = "active directory msi"; + private const string ActiveDirectoryDefault = "active directory default"; private readonly string _typeName; private readonly IReadOnlyCollection _authenticationsWithAppSpecifiedProvider; @@ -46,6 +47,7 @@ private static void SetDefaultAuthProviders(SqlAuthenticationProviderManager ins instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider); instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDefault, activeDirectoryAuthProvider); } } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 897fc37b5a..7e7ca226e7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -193,11 +193,15 @@ public SqlConnection(string connectionString, SqlCredential credential) : this() } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } Credential = credential; @@ -508,6 +512,11 @@ private bool UsesActiveDirectoryMSI(SqlConnectionString opt) return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; } + private bool UsesActiveDirectoryDefault(SqlConnectionString opt) + { + return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; + } + private bool UsesAuthentication(SqlConnectionString opt) { return opt != null && opt.Authentication != SqlAuthenticationMethod.NotSpecified; @@ -565,7 +574,7 @@ public override string ConnectionString if (_credential != null) { // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -582,11 +591,15 @@ public override string ConnectionString } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); @@ -878,7 +891,7 @@ public SqlCredential Credential { var connectionOptions = (SqlConnectionString)ConnectionOptions; // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -895,11 +908,15 @@ public SqlCredential Credential } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index d2d2cf7891..7752feb4f0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -484,12 +484,17 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && HasPasswordKeyword) { - throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && HasPasswordKeyword) { - throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && HasPasswordKeyword) + { + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 3935ed01e4..8f1ba312af 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1308,6 +1308,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault // Since AD Integrated may be acting like Windows integrated, additionally check _fedAuthRequired || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired)) { @@ -2116,6 +2117,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); Debug.Assert(fedAuthInfo != null, "info should not be null."); @@ -2358,6 +2360,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: case SqlAuthenticationMethod.ActiveDirectoryMSI: + case SqlAuthenticationMethod.ActiveDirectoryDefault: if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 8a0f5293b4..0ede264635 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -283,9 +283,9 @@ internal static Exception DeviceFlowWithUsernamePassword() { return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_DeviceFlowWithUsernamePassword)); } - internal static Exception ManagedIdentityWithPassword(string authenticationMode) + internal static Exception NonInteractiveWithPassword(string authenticationMode) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_ManagedIdentityWithPassword, authenticationMode)); + return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_NonInteractiveWithPassword, authenticationMode)); } static internal Exception SettingIntegratedWithCredential() { @@ -299,9 +299,9 @@ static internal Exception SettingDeviceFlowWithCredential() { return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingDeviceFlowWithCredential)); } - static internal Exception SettingManagedIdentityWithCredential(string authenticationMode) + static internal Exception SettingNonInteractiveWithCredential(string authenticationMode) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingManagedIdentityWithCredential, authenticationMode)); + return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingNonInteractiveWithCredential, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedArgument() { @@ -315,9 +315,9 @@ static internal Exception SettingCredentialWithDeviceFlowArgument() { return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityArgument(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveArgument(string authenticationMode) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedInvalid() { @@ -331,9 +331,9 @@ static internal Exception SettingCredentialWithDeviceFlowInvalid() { return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityInvalid(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveInvalid(string authenticationMode) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } internal static Exception NullEmptyTransactionName() { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 9099f5882c..44a8f02941 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -252,6 +252,7 @@ public enum FedAuthLibrary : byte public const byte MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL = 0x01; // Using the Password byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW = 0x03; // Using the Interactive byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03; // Using the Interactive byte as that's supported for Identity based authentication + public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes public enum ActiveDirectoryWorkflow : byte { @@ -261,6 +262,7 @@ public enum ActiveDirectoryWorkflow : byte ServicePrincipal = MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL, DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, + Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, } // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. @@ -1155,7 +1157,10 @@ public enum SqlAuthenticationMethod ActiveDirectoryManagedIdentity, /// - ActiveDirectoryMSI + ActiveDirectoryMSI, + + /// + ActiveDirectoryDefault } // This enum indicates the state of TransparentNetworkIPResolution // The first attempt when TNIR is on should be sequential. If the first attempt failes next attempts should be parallel. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 210b5944cf..b32a6e3174 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -7794,6 +7794,9 @@ internal int WriteSessionRecoveryFeatureRequest(SessionData reconnectData, bool case SqlAuthenticationMethod.ActiveDirectoryMSI: workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY; break; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT; + break; default: Debug.Assert(false, "Unrecognized Authentication type for fedauth MSAL request"); break; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index 9318d7eb40..db5c145e9a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -2799,9 +2799,9 @@ internal class Strings { /// /// Looks up a localized string similar to Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords.. /// - internal static string SQL_ManagedIdentityWithPassword { + internal static string SQL_NonInteractiveWithPassword { get { - return ResourceManager.GetString("SQL_ManagedIdentityWithPassword", resourceCulture); + return ResourceManager.GetString("SQL_NonInteractiveWithPassword", resourceCulture); } } @@ -3087,9 +3087,9 @@ internal class Strings { /// /// Looks up a localized string similar to Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string.. /// - internal static string SQL_SettingCredentialWithManagedIdentity { + internal static string SQL_SettingCredentialWithNonInteractive { get { - return ResourceManager.GetString("SQL_SettingCredentialWithManagedIdentity", resourceCulture); + return ResourceManager.GetString("SQL_SettingCredentialWithNonInteractive", resourceCulture); } } @@ -3123,9 +3123,9 @@ internal class Strings { /// /// Looks up a localized string similar to Cannot use 'Authentication={0}', if the Credential property has been set.. /// - internal static string SQL_SettingManagedIdentityWithCredential { + internal static string SQL_SettingNonInteractiveWithCredential { get { - return ResourceManager.GetString("SQL_SettingManagedIdentityWithCredential", resourceCulture); + return ResourceManager.GetString("SQL_SettingNonInteractiveWithCredential", resourceCulture); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index 335803c097..1c4aa25d59 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -411,7 +411,7 @@ Cannot use 'Authentication=Active Directory Device Code Flow' with 'User ID', 'UID', 'Password' or 'PWD' connection string keywords. - + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. @@ -1908,13 +1908,13 @@ Cannot set the Credential property if 'Authentication=Active Directory Device Code Flow' has been specified in the connection string. - + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Cannot use 'Authentication=Active Directory Device Code Flow', if the Credential property has been set. - + Cannot use 'Authentication={0}', if the Credential property has been set. diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 64f25ac54f..eac679eece 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -117,6 +117,8 @@ public enum SqlAuthenticationMethod ActiveDirectoryManagedIdentity = 7, /// ActiveDirectoryMSI = 8, + /// + ActiveDirectoryDefault = 9, /// NotSpecified = 0, /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index ec0bd3a558..f37aed4a2d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -57,7 +57,8 @@ internal ReadOnlyCollection(T[] items) { _items = items; #if DEBUG - for(int i = 0; i < items.Length; ++i) { + for (int i = 0; i < items.Length; ++i) + { Debug.Assert(null != items[i], "null item"); } #endif @@ -524,12 +525,48 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj const string ActiveDirectoryDeviceCodeFlowString = "Active Directory Device Code Flow"; internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity"; internal const string ActiveDirectoryMSIString = "Active Directory MSI"; - const string SqlCertificateString = "Sql Certificate"; + internal const string ActiveDirectoryDefaultString = "Active Directory Default"; + // const string SqlCertificateString = "Sql Certificate"; - internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) +#if DEBUG + private static string[] s_supportedAuthenticationModes = { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed"); + "NotSpecified", + "SqlPassword", + "ActiveDirectoryPassword", + "ActiveDirectoryIntegrated", + "ActiveDirectoryInteractive", + "ActiveDirectoryServicePrincipal", + "ActiveDirectoryDeviceCodeFlow", + "ActiveDirectoryManagedIdentity", + "ActiveDirectoryMSI", + "ActiveDirectoryDefault" + }; + + private static bool IsValidAuthenticationMethodEnum() + { + string[] names = Enum.GetNames(typeof(SqlAuthenticationMethod)); + int l = s_supportedAuthenticationModes.Length; + bool listValid; + if (listValid = names.Length == l) + { + for (int i = 0; i < l; i++) + { + if (s_supportedAuthenticationModes[i].CompareTo(names[i]) != 0) + { + listValid = false; + } + } + } + return listValid; + } +#endif + internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) + { +#if DEBUG + Debug.Assert(IsValidAuthenticationMethodEnum(), "SqlAuthenticationMethod enum has changed, update needed"); +#endif bool isSuccess = false; if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlPasswordString) @@ -580,6 +617,12 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent result = SqlAuthenticationMethod.ActiveDirectoryMSI; isSuccess = true; } + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryDefaultString) + || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryDefault, CultureInfo.InvariantCulture))) + { + result = SqlAuthenticationMethod.ActiveDirectoryDefault; + isSuccess = true; + } #if ADONET_CERT_AUTH else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlCertificateString) || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.SqlCertificate, CultureInfo.InvariantCulture))) { @@ -671,6 +714,7 @@ internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod valu || value == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || value == SqlAuthenticationMethod.ActiveDirectoryMSI + || value == SqlAuthenticationMethod.ActiveDirectoryDefault #if ADONET_CERT_AUTH || value == SqlAuthenticationMethod.SqlCertificate #endif @@ -699,6 +743,8 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value) return ActiveDirectoryManagedIdentityString; case SqlAuthenticationMethod.ActiveDirectoryMSI: return ActiveDirectoryMSIString; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + return ActiveDirectoryDefaultString; #if ADONET_CERT_AUTH case SqlAuthenticationMethod.SqlCertificate: return SqlCertificateString; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index 561c0cd101..d0ab1507d9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -22,6 +22,7 @@ internal class SqlAuthenticationProviderManager private const string ActiveDirectoryDeviceCodeFlow = "active directory device code flow"; private const string ActiveDirectoryManagedIdentity = "active directory managed identity"; private const string ActiveDirectoryMSI = "active directory msi"; + private const string ActiveDirectoryDefault = "active directory default"; static SqlAuthenticationProviderManager() { @@ -51,6 +52,7 @@ static SqlAuthenticationProviderManager() Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider); + Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDefault, activeDirectoryAuthProvider); } public static readonly SqlAuthenticationProviderManager Instance; @@ -221,6 +223,8 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe return SqlAuthenticationMethod.ActiveDirectoryManagedIdentity; case ActiveDirectoryMSI: return SqlAuthenticationMethod.ActiveDirectoryMSI; + case ActiveDirectoryDefault: + return SqlAuthenticationMethod.ActiveDirectoryDefault; default: throw SQL.UnsupportedAuthentication(authentication); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 427e5f71f4..bec4d7e96a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -399,12 +399,17 @@ public SqlConnection(string connectionString, SqlCredential credential) : this() if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + + if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } Credential = credential; @@ -618,6 +623,11 @@ private bool UsesActiveDirectoryMSI(SqlConnectionString opt) return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; } + private bool UsesActiveDirectoryDefault(SqlConnectionString opt) + { + return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; + } + private bool UsesAuthentication(SqlConnectionString opt) { return opt != null && opt.Authentication != SqlAuthenticationMethod.NotSpecified; @@ -776,7 +786,7 @@ override public string ConnectionString if (_credential != null) { // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -793,11 +803,15 @@ override public string ConnectionString } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); @@ -1115,7 +1129,7 @@ public SqlCredential Credential { var connectionOptions = (SqlConnectionString)ConnectionOptions; // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -1132,11 +1146,15 @@ public SqlCredential Credential } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 761ab74751..bc01c7491c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -576,12 +576,17 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && HasPasswordKeyword) { - throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && HasPasswordKeyword) { - throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && HasPasswordKeyword) + { + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } #if ADONET_CERT_AUTH diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index e66081de70..42a6eec32c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1580,6 +1580,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault // Since AD Integrated may be acting like Windows integrated, additionally check _fedAuthRequired || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired)) { @@ -1975,7 +1976,8 @@ private bool ShouldDisableTnir(SqlConnectionString connectionOptions) connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || - connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; + connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || + connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; // Check if the user had explicitly specified the TNIR option in the connection string or the connection string builder. // If the user has specified the option in the connection string explicitly, then we shouldn't disable TNIR. @@ -2563,6 +2565,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); @@ -2795,6 +2798,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: case SqlAuthenticationMethod.ActiveDirectoryMSI: + case SqlAuthenticationMethod.ActiveDirectoryDefault: if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index bdec925681..3dd69c5299 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -334,9 +334,9 @@ static internal Exception DeviceFlowWithUsernamePassword() { return ADP.Argument(StringsHelper.GetString(Strings.SQL_DeviceFlowWithUsernamePassword)); } - static internal Exception ManagedIdentityWithPassword(string authenticationMode) + static internal Exception NonInteractiveWithPassword(string authenticationMode) { - return ADP.Argument(StringsHelper.GetString(Strings.SQL_ManagedIdentityWithPassword, authenticationMode)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_NonInteractiveWithPassword, authenticationMode)); } static internal Exception SettingIntegratedWithCredential() { @@ -350,9 +350,9 @@ static internal Exception SettingDeviceFlowWithCredential() { return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingDeviceFlowWithCredential)); } - static internal Exception SettingManagedIdentityWithCredential(string authenticationMode) + static internal Exception SettingNonInteractiveWithCredential(string authenticationMode) { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingManagedIdentityWithCredential, authenticationMode)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingNonInteractiveWithCredential, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedArgument() { @@ -366,9 +366,9 @@ static internal Exception SettingCredentialWithDeviceFlowArgument() { return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityArgument(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveArgument(string authenticationMode) { - return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedInvalid() { @@ -382,9 +382,9 @@ static internal Exception SettingCredentialWithDeviceFlowInvalid() { return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityInvalid(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveInvalid(string authenticationMode) { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } static internal Exception InvalidSQLServerVersionUnknown() { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 4ac0a23fa4..417657eb49 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -244,6 +244,7 @@ public enum FedAuthLibrary : byte public const byte MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL = 0x01; // Using the Password byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW = 0x03; // Using the Interactive byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03; // Using the Interactive byte as that's supported for Identity based authentication + public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes public enum ActiveDirectoryWorkflow : byte { @@ -253,6 +254,7 @@ public enum ActiveDirectoryWorkflow : byte ServicePrincipal = MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL, DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, + Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, } // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. @@ -1118,6 +1120,9 @@ public enum SqlAuthenticationMethod /// ActiveDirectoryMSI, + + /// + ActiveDirectoryDefault, #if ADONET_CERT_AUTH SqlCertificate #endif diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 84aa07f196..0d3c877b7f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -554,6 +554,9 @@ internal void ProcessPendingAck(TdsParserStateObject stateObj) case SqlAuthenticationMethod.ActiveDirectoryMSI: SqlClientEventSource.Log.TryTraceEvent(" Active Directory MSI authentication"); break; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + SqlClientEventSource.Log.TryTraceEvent(" Active Directory Default authentication"); + break; case SqlAuthenticationMethod.SqlPassword: SqlClientEventSource.Log.TryTraceEvent(" SQL Password authentication"); break; @@ -8587,6 +8590,9 @@ internal int WriteSessionRecoveryFeatureRequest(SessionData reconnectData, bool case SqlAuthenticationMethod.ActiveDirectoryMSI: workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY; break; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT; + break; default: Debug.Assert(false, "Unrecognized Authentication type for fedauth MSAL request"); break; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index f5b5f44a4c..050b0ee596 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -9642,9 +9642,9 @@ internal class Strings { /// /// Looks up a localized string similar to Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords.. /// - internal static string SQL_ManagedIdentityWithPassword { + internal static string SQL_NonInteractiveWithPassword { get { - return ResourceManager.GetString("SQL_ManagedIdentityWithPassword", resourceCulture); + return ResourceManager.GetString("SQL_NonInteractiveWithPassword", resourceCulture); } } @@ -9984,9 +9984,9 @@ internal class Strings { /// /// Looks up a localized string similar to Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string.. /// - internal static string SQL_SettingCredentialWithManagedIdentity { + internal static string SQL_SettingCredentialWithNonInteractive { get { - return ResourceManager.GetString("SQL_SettingCredentialWithManagedIdentity", resourceCulture); + return ResourceManager.GetString("SQL_SettingCredentialWithNonInteractive", resourceCulture); } } @@ -10020,9 +10020,9 @@ internal class Strings { /// /// Looks up a localized string similar to Cannot use 'Authentication={0}', if the Credential property has been set.. /// - internal static string SQL_SettingManagedIdentityWithCredential { + internal static string SQL_SettingNonInteractiveWithCredential { get { - return ResourceManager.GetString("SQL_SettingManagedIdentityWithCredential", resourceCulture); + return ResourceManager.GetString("SQL_SettingNonInteractiveWithCredential", resourceCulture); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index c12e0f5f98..cb86d1f8f0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -2502,7 +2502,7 @@ "Authentication=Active Directory Device Code Flow" kann nicht mit den Schlüsselwörtern "User ID", "UID", "Password" oder "PWD" für die Verbindungszeichenfolge verwendet werden. - + "Authentication={0}" kann nicht mit den Schlüsselwörtern "Password" oder "PWD" für die Verbindungszeichenfolge verwendet werden. @@ -4575,13 +4575,13 @@ Die Eigenschaft "Credential" kann nicht festgelegt werden, wenn in der Verbindungszeichenfolge "Authentication=Active Directory Device Code Flow" angegeben wurde. - + Die Eigenschaft "Credential" kann nicht festgelegt werden, wenn in der Verbindungszeichenfolge "Authentication={0}" angegeben wurde. "Authentication=Active Directory Device Code Flow" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. - + "Authentication={0}" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index 24e6ae4ed9..1dc17be275 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -2502,7 +2502,7 @@ No se puede usar "Authentication=Active Directory Device Code Flow" con las palabras clave de cadena de conexión "User ID", "UID", "Password" ni "PWD". - + No se puede usar "Authentication={0}" con las palabras clave de cadena de conexión "Password" ni "PWD". @@ -4575,13 +4575,13 @@ No se puede establecer la propiedad Credential si se ha especificado "Authentication=Active Directory Device Code Flow" en la cadena de conexión. - + No se puede establecer la propiedad Credential si se ha especificado "Authentication={0}" en la cadena de conexión. No se puede usar "Active Directory Device Code Flow" si se ha establecido la propiedad Credential. - + No se puede usar "Authentication={0}" si se ha establecido la propiedad Credential. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index ab89be51e6..a1b40c474c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -2502,7 +2502,7 @@ Impossible d'utiliser « Authentication=Active Directory Device Code Flow » avec les mots clés de chaîne de connexion « User ID », « UID », « Password » et « PWD ». - + Impossible d'utiliser « Authentication={0} » avec les mots clés de chaîne de connexion « Password » ou « PWD ». @@ -4575,13 +4575,13 @@ Impossible de définir la propriété Credential si « Authentication=Active Directory Device Code Flow » est spécifié dans la chaîne de connexion. - + Impossible de définir la propriété Credential si « Authentication={0} » a été spécifié dans la chaîne de connexion. Impossible d'utiliser « Authentication=Active Directory Device Code Flow » si la propriété Credential est définie. - + Impossible d'utiliser « Authentication={0} », si la propriété Credential a été définie. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index b9ba1c3b11..9b3806809c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -2502,7 +2502,7 @@ Non è possibile usare 'Authentication=Active Directory Device Code Flow' con le parole chiave della stringa di connessione 'User ID', 'UID', 'Password' o 'PWD'. - + Non è possibile usare 'Authentication={0}' con le parole chiave della stringa di connessione 'Password' o 'PWD'. @@ -4575,13 +4575,13 @@ Non è possibile impostare la proprietà Credential se nella stringa di connessione è stato specificato 'Authentication=Active Directory Device Code Flow'. - + Non è possibile impostare la proprietà Credential se nella stringa di connessione è stato specificato 'Authentication={0}'. Non è possibile usare 'Authentication=Active Directory Device Code Flow' se è stata impostata la proprietà Credential. - + Non è possibile usare 'Authentication={0}' se è stata impostata la proprietà Credential. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index 99cf38ae8d..e0c2159d01 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -2502,7 +2502,7 @@ 'Authentication=Active Directory Device Code Flow' と接続文字列キーワード 'User ID'、'UID'、'Password'、'PWD' を一緒に使用することはできません。 - + 'Authentication={0}' と接続文字列キーワード 'Password'、'PWD' を一緒に使用することはできません。 @@ -4575,13 +4575,13 @@ 接続文字列で 'Authentication=Active Directory Device Code Flow' が指定されている場合は、Credential プロパティを設定できません。 - + 接続文字列で 'Authentication={0}' が指定されている場合は、Credential プロパティを設定できません。 Credential プロパティが設定されている場合は、'Authentication=Active Directory Device Code Flow' を使用できません。 - + Credential プロパティが設定されている場合は、'Authentication={0}' を使用できません。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 5938ffb922..8c779955d5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -2502,7 +2502,7 @@ 'Authentication=Active Directory Device Code Flow'를 'User ID', 'UID', 'Password' 또는 'PWD' 연결 문자열 키워드와 함께 사용할 수 없습니다. - + 'Authentication={0}'을(를) 'Password' 또는 'PWD' 연결 문자열 키워드와 함께 사용할 수 없습니다. @@ -4575,13 +4575,13 @@ 'Authentication=Active Directory Device Code Flow'가 연결 문자열에 지정된 경우 Credential 속성을 설정할 수 없습니다. - + 'Authentication={0}'이(가) 연결 문자열에 지정된 경우 자격 증명 속성을 설정할 수 없습니다. Credential 속성이 설정된 경우 'Authentication=Active Directory Device Code Flow'를 사용할 수 없습니다. - + 자격 증명 속성이 설정된 경우 'Authentication={0}'을(를) 사용할 수 없습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index 388e5b5d34..e5a0b36503 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -2502,7 +2502,7 @@ Não é possível usar 'Authentication=Active Directory Device Code Flow' com as palavras-chave de cadeia de conexão 'User ID', 'UID', 'Password' ou 'PWD'. - + Não é possível usar 'Authentication={0}' com as palavras-chave de cadeia de conexão 'Password' ou 'PWD'. @@ -4575,13 +4575,13 @@ Não é possível definir a propriedade Credential quando 'Authentication=Active Directory Device Code Flow' está especificado na cadeia de conexão. - + Não é possível definir a propriedade Credential quando 'Authentication={0}' é especificado na cadeia de conexão. Não é possível usar 'Authentication=Active Directory Device Code Flow' quando a propriedade Credential está configurada. - + Não é possível usar 'Authentication={0}' quando a propriedade Credential está configurada. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 7ca22b2fe8..55505e08d4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -2502,7 +2502,7 @@ Cannot use 'Authentication=Active Directory Device Code Flow' with 'User ID', 'UID', 'Password' or 'PWD' connection string keywords. - + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. @@ -4578,13 +4578,13 @@ Cannot set the Credential property if 'Authentication=Active Directory Device Code Flow' has been specified in the connection string. - + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Cannot use 'Authentication=Active Directory Device Code Flow', if the Credential property has been set. - + Cannot use 'Authentication={0}', if the Credential property has been set. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index e45e6d3ad5..73a96370f8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -2502,7 +2502,7 @@ Невозможно использовать "Authentication=Active Directory Device Code Flow" с ключевыми словами строки подключения "User ID", "UID", "Password" или "PWD". - + Невозможно использовать "Authentication={0}" с ключевыми словами Password или PWD в строке подключения. @@ -4575,13 +4575,13 @@ Не удается задать свойство Credential, если в строке подключения указано "Authentication=Active Directory Device Code Flow". - + Невозможно задать свойство Credential, если в строке подключения указано "Authentication={0}". Невозможно использовать ", если задано свойство Credential. - + Невозможно использовать "Authentication={0}", если задано свойство Credential. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 9270e77566..202dd8dddc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -2502,7 +2502,7 @@ 无法结合使用 "Authentication=Active Directory Device Code Flow" 与 "User ID"、"UID"、"Password" 或 "PWD" 连接字符串关键字。 - + 无法将 "Authentication={0}" 与 "Password" 或 "PWD" 连接字符串关键字结合使用。 @@ -4575,13 +4575,13 @@ 如果在连接字符串中指定了 "Authentication=Active Directory Device Code Flow",则无法设置 Credential 属性。 - + 如果在连接字符串中指定了 "Authentication={0}",则无法设置 Credential 属性。 如果设置了 Credential 属性,则无法使用 "Authentication=Active Directory Device Code Flow"。 - + 如果设置了 Credential 属性,则无法使用 "Authentication={0}"。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 21493e1e79..f0c30aabc5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -2502,7 +2502,7 @@ 無法搭配 'User ID'、'UID'、'Password' 或 'PWD' 連接字串關鍵字使用 'Authentication=Active Directory Device Code Flow'。 - + 使用 'Authentication={0}' 時,無法搭配 'Password' 或 'PWD' 連接字串關鍵字一起使用。 @@ -4575,13 +4575,13 @@ 如果連接字串中已指定 'Authentication=Active Directory Device Code Flow',則無法設定 Credential 屬性。 - + 如果連接字串中已指定 'Authentication={0}',則無法設定 Credential 屬性。 如果已設定 Credential 屬性,則無法使用 'Authentication=Active Directory Device Code Flow'。 - + 如果已設定 Credential 屬性,就無法使用 'Authentication={0}'。 diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs index 65aeec11c8..25f591f1c2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; -using System.Linq; using System.Security; using System.Threading; using System.Threading.Tasks; @@ -73,7 +72,8 @@ public override bool IsSupported(SqlAuthenticationMethod authentication) || authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity - || authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; + || authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; } /// @@ -117,12 +117,31 @@ public override async Task AcquireTokenAsync(SqlAuthenti string tenantId = parameters.Authority.Substring(seperatorIndex + 1); string authority = parameters.Authority.Remove(seperatorIndex + 1); - TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions() { AuthorityHost = new Uri(authority) }; TokenRequestContext tokenRequestContext = new TokenRequestContext(scopes); + string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId; + + if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDefault) + { + DefaultAzureCredentialOptions defaultAzureCredentialOptions = new DefaultAzureCredentialOptions() + { + AuthorityHost = new Uri(authority), + ManagedIdentityClientId = clientId, + InteractiveBrowserTenantId = tenantId, + SharedTokenCacheTenantId = tenantId, + SharedTokenCacheUsername = clientId, + VisualStudioCodeTenantId = tenantId, + VisualStudioTenantId = tenantId, + ExcludeInteractiveBrowserCredential = true // Force disabled, even though it's disabled by default to respect driver specifications. + }; + AccessToken accessToken = await new DefaultAzureCredential(defaultAzureCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Default auth mode. Expiry Time: {0}", accessToken.ExpiresOn); + return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); + } + + TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions() { AuthorityHost = new Uri(authority) }; if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryMSI) { - string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId; AccessToken accessToken = await new ManagedIdentityCredential(clientId, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Managed Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn); return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); @@ -195,15 +214,28 @@ public override async Task AcquireTokenAsync(SqlAuthenti parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow) { // Fetch available accounts from 'app' instance - System.Collections.Generic.IEnumerable accounts = await app.GetAccountsAsync(); - IAccount account; - if (!string.IsNullOrEmpty(parameters.UserId)) + System.Collections.Generic.IEnumerator accounts = (await app.GetAccountsAsync()).GetEnumerator(); + + IAccount account = default; + if (accounts.MoveNext()) { - account = accounts.FirstOrDefault(a => parameters.UserId.Equals(a.Username, System.StringComparison.InvariantCultureIgnoreCase)); - } - else - { - account = accounts.FirstOrDefault(); + if (!string.IsNullOrEmpty(parameters.UserId)) + { + do + { + IAccount currentVal = accounts.Current; + if (string.Compare(parameters.UserId, currentVal.Username, StringComparison.InvariantCultureIgnoreCase) == 0) + { + account = currentVal; + break; + } + } + while (accounts.MoveNext()); + } + else + { + account = accounts.Current; + } } if (null != account) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs index 4a61e10edd..5c44a5830c 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs @@ -29,6 +29,8 @@ public partial class SqlConnectionStringBuilderTest [InlineData("Authentication = ActiveDirectoryManagedIdentity ")] [InlineData("Authentication = Active Directory MSI ")] [InlineData("Authentication = ActiveDirectoryMSI ")] + [InlineData("Authentication = Active Directory Default ")] + [InlineData("Authentication = ActiveDirectoryDefault ")] [InlineData("Command Timeout = 5")] [InlineData("Command Timeout = 15")] [InlineData("Command Timeout = 0")] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs index d487b187fb..5723958636 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -427,6 +427,52 @@ public static void ActiveDirectoryMSIWithPasswordMustFail() Assert.Contains(expectedMessage, e.Message); } + [ConditionalFact(nameof(IsAADConnStringsSetup))] + public static void ActiveDirectoryDefaultWithCredentialsMustFail() + { + // connection fails with expected error message. + string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; + string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + + "Authentication=Active Directory Default;"; + + SecureString str = new SecureString(); + foreach (char c in "hello") + { + str.AppendChar(c); + } + str.MakeReadOnly(); + SqlCredential credential = new SqlCredential("someuser", str); + InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred, credential)); + + string expectedMessage = "Cannot set the Credential property if 'Authentication=Active Directory Default' has been specified in the connection string."; + Assert.Contains(expectedMessage, e.Message); + } + + [ConditionalFact(nameof(IsAADConnStringsSetup))] + public static void ActiveDirectoryDefaultWithPasswordMustFail() + { + // connection fails with expected error message. + string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; + string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + + "Authentication=ActiveDirectoryDefault; Password=anything"; + + ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); + + string expectedMessage = "Cannot use 'Authentication=Active Directory Default' with 'Password' or 'PWD' connection string keywords."; + Assert.Contains(expectedMessage, e.Message); + } + + [ConditionalFact(nameof(IsAADConnStringsSetup))] + public static void ActiveDirectoryDefaultMustPass() + { + string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; + string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + + "Authentication=ActiveDirectoryDefault;"; + + // Connection should be established using Managed Identity by default. + ConnectAndDisconnect(connStr); + } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsIntegratedSecuritySetup), nameof(DataTestUtility.AreConnStringsSetup))] public static void ADInteractiveUsingSSPI() { diff --git a/src/Microsoft.Data.SqlClient/tests/NuGet.config b/src/Microsoft.Data.SqlClient/tests/NuGet.config new file mode 100644 index 0000000000..366141ab39 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/NuGet.config @@ -0,0 +1,8 @@ + + + + + + + + From ca4957c49e853aca8f3a6a890fe042673128b03d Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 17 May 2021 16:59:16 -0700 Subject: [PATCH 36/87] Update error messages for Enclave with forward links (#994) --- .../netcore/src/Resources/Strings.Designer.cs | 24 +++++------ .../netcore/src/Resources/Strings.resx | 26 ++++++------ .../netfx/src/Resources/Strings.Designer.cs | 40 +++++++++---------- .../netfx/src/Resources/Strings.resx | 26 ++++++------ 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index db5c145e9a..e043593408 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -889,7 +889,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature computed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services.. + /// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature computed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services.. /// internal static string AttestationTokenSignatureValidationFailed { get { @@ -1006,7 +1006,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance.. + /// Looks up a localized string similar to The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details.. /// internal static string GetAttestationSigningCertificateFailedInvalidCertificate { get { @@ -1015,7 +1015,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized. For more information contact Customer Support Services. Error details: '{1}'.. + /// Looks up a localized string similar to The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For more information contact Customer Support Services. Error details: '{1}'.. /// internal static string GetAttestationSigningCertificateRequestFailedFormat { get { @@ -1087,7 +1087,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy. If the policy is correct, contact Customer Support Services.. + /// Looks up a localized string similar to The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services.. /// internal static string InvalidClaimInAttestationToken { get { @@ -1285,7 +1285,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy. If the policy is correct, contact Customer Support Services.. + /// Looks up a localized string similar to The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services.. /// internal static string MissingClaimInAttestationToken { get { @@ -4300,7 +4300,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to You have specified the attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// Looks up a localized string similar to You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details.. /// internal static string TCE_AttestationProtocolNotSupported { get { @@ -4318,7 +4318,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details.. /// internal static string TCE_AttestationURLNotSupported { get { @@ -4594,7 +4594,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// Looks up a localized string similar to You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details.. /// internal static string TCE_EnclaveComputationsNotSupported { get { @@ -4621,7 +4621,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance.. + /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details.. /// internal static string TCE_EnclaveTypeNotReturned { get { @@ -5467,7 +5467,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}'.. + /// Looks up a localized string similar to Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details.. /// internal static string VerifyEnclavePolicyFailedFormat { get { @@ -5476,7 +5476,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services.. + /// Looks up a localized string similar to Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services.. /// internal static string VerifyEnclaveReportFailed { get { @@ -5494,7 +5494,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services.. + /// Looks up a localized string similar to Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services.. /// internal static string VerifyHealthCertificateChainFormat { get { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index 1c4aa25d59..14a70b74a3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1657,13 +1657,13 @@ Invalid key store provider name specified. Key store provider names cannot be null or empty. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - You have specified the attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. No enclave provider found for enclave type '{0}' and attestation protocol '{1}'. Please specify the correct attestation protocol in the connection string. @@ -1672,7 +1672,7 @@ Executing a query requires enclave computations, but the application configuration is missing the enclave provider section. - You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. Internal Error. Enclave type received from SQL Server is null or empty when executing a query requiring enclave computations. @@ -1786,7 +1786,7 @@ Globalization Invariant Mode is not supported. - The validation of an attestation token failed. The token signature does not match the signature computed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services. + The validation of an attestation token failed. The token signature does not match the signature computed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services. @@ -1807,10 +1807,10 @@ The validation of an attestation token failed. The token has an invalid format. Contact Customer Support Services. Error details: '{0}'. - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized. For more information contact Customer Support Services. Error details: '{1}'. + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For more information contact Customer Support Services. Error details: '{1}'. The validation of an attestation token failed. Cannot retrieve a public key from the attestation public key endpoint, or the retrieved key has an invalid format. Error details: '{0}'. @@ -1828,25 +1828,25 @@ The validation of the attestation token has failed during signature validation. Exception: '{0}'. - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy. If the policy is correct, contact Customer Support Services. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy. If the policy is correct, contact Customer Support Services. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. Failed to check if the enclave is running in the production mode. Contact Customer Support Services. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}'. + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. The enclave report received from SQL Server is not in the correct format. Contact Customer Support Services. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. Specifies an attestation protocol for its corresponding enclave attestation service. @@ -1932,4 +1932,4 @@ '{0}' is not less than '{1}'; '{2}' cannot be greater than '{3}'. - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 050b0ee596..e24b20f639 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -1824,6 +1824,15 @@ internal class Strings { } } + /// + /// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services.. + /// + internal static string AttestationTokenSignatureValidationFailed { + get { + return ResourceManager.GetString("AttestationTokenSignatureValidationFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Destination array is not long enough to copy all the items in the collection. Check array index and length.. /// @@ -1860,15 +1869,6 @@ internal class Strings { } } - /// - /// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services.. - /// - internal static string AttestationTokenSignatureValidationFailed { - get { - return ResourceManager.GetString("AttestationTokenSignatureValidationFailed", resourceCulture); - } - } - /// /// Looks up a localized string similar to .database.chinacloudapi.cn. /// @@ -6721,7 +6721,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance.. + /// Looks up a localized string similar to The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details.. /// internal static string GetAttestationSigningCertificateFailedInvalidCertificate { get { @@ -6730,7 +6730,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized. For more information contact Customer Support Services. Error details: '{1}'.. + /// Looks up a localized string similar to The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'.. /// internal static string GetAttestationSigningCertificateRequestFailedFormat { get { @@ -6811,7 +6811,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy. If the policy is correct, contact Customer Support Services.. + /// Looks up a localized string similar to The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services.. /// internal static string InvalidClaimInAttestationToken { get { @@ -7081,7 +7081,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy. If the policy is correct, contact Customer Support Services.. + /// Looks up a localized string similar to The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services.. /// internal static string MissingClaimInAttestationToken { get { @@ -11935,7 +11935,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to You have specified the attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// Looks up a localized string similar to You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details.. /// internal static string TCE_AttestationProtocolNotSupported { get { @@ -11953,7 +11953,7 @@ internal class Strings { } /// - /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details.. /// internal static string TCE_AttestationURLNotSupported { get { @@ -12247,7 +12247,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// Looks up a localized string similar to You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details.. /// internal static string TCE_EnclaveComputationsNotSupported { get { @@ -12274,7 +12274,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance.. + /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details.. /// internal static string TCE_EnclaveTypeNotReturned { get { @@ -13183,7 +13183,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}'.. + /// Looks up a localized string similar to Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details.. /// internal static string VerifyEnclavePolicyFailedFormat { get { @@ -13192,7 +13192,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services.. + /// Looks up a localized string similar to Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services.. /// internal static string VerifyEnclaveReportFailed { get { @@ -13210,7 +13210,7 @@ internal static string TCE_DbConnectionString_IPAddressPreference } /// - /// Looks up a localized string similar to Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services.. + /// Looks up a localized string similar to Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services.. /// internal static string VerifyHealthCertificateChainFormat { get { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 55505e08d4..522f305696 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4345,16 +4345,16 @@ {0} instance in use does not support column encryption. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - You have specified the attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates. @@ -4459,7 +4459,7 @@ Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services. + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services. @@ -4480,10 +4480,10 @@ The validation of an attestation token failed. The token has an invalid format. Contact Customer Support Services. Error details: '{0}'. - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized. For more information contact Customer Support Services. Error details: '{1}'. + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. The validation of an attestation token failed. Cannot retrieve a public key from the attestation public key endpoint, or the retrieved key has an invalid format. Error details: '{0}'. @@ -4501,25 +4501,25 @@ The validation of the attestation token has failed during signature validation. Exception: '{0}'. - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy. If the policy is correct, contact Customer Support Services. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy. If the policy is correct, contact Customer Support Services. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. Failed to check if the enclave is running in the production mode. Contact Customer Support Services. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}'. + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. The enclave report received from SQL Server is not in the correct format. Contact Customer Support Services. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. Specifies an attestation protocol for its corresponding enclave attestation service. @@ -4614,4 +4614,4 @@ Non-negative number required. - \ No newline at end of file + From 00d45e87ab45b5d7fab334baf65631a64ae20aaf Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 17 May 2021 16:59:40 -0700 Subject: [PATCH 37/87] Fixes corrupted connection issue when an exception occurs during RPC execution with TVP types (#1068) --- build.proj | 2 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 6 -- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 7 -- .../ManualTests/SQL/ParameterTest/TvpTest.cs | 96 ++++++++++++++++++- 4 files changed, 96 insertions(+), 15 deletions(-) diff --git a/build.proj b/build.proj index 9d59894bf0..763e975df1 100644 --- a/build.proj +++ b/build.proj @@ -38,7 +38,7 @@ - + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index b32a6e3174..5c526e9c30 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -9110,13 +9110,7 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques } catch (Exception e) { - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - FailureCleanup(stateObj, e); - throw; } FinalizeExecuteRPC(stateObj); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 0d3c877b7f..328867ddca 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -10449,14 +10449,7 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques } catch (Exception e) { - // UNDONE - should not be catching all exceptions!!! - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - FailureCleanup(stateObj, e); - throw; } FinalizeExecuteRPC(stateObj); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs index 7c4e81cfd5..084fe11789 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs @@ -17,6 +17,7 @@ using System.Transactions; using Microsoft.Data.SqlClient.Server; using Xunit; +using System.Linq; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { @@ -82,6 +83,99 @@ public void TestPacketNumberWraparound() Assert.True(enumerator.MaxCount == enumerator.Count); } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + public void TestConnectionIsSafeToReuse() + { + using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + + // Bad Scenario - exception expected. + try + { + List list = new() + { + new Item(0), + null, + new Item(2), + new Item(3), + new Item(4), + new Item(5) + }; + + IEnumerable Ids = list.Select(x => x.id.Value).Distinct(); + + var sqlParam = new SqlParameter("ids", SqlDbType.Structured) + { + TypeName = "dbo.TableOfIntId", + SqlValue = Ids.Select(x => + { + SqlDataRecord rec = new(new[] { new SqlMetaData("Id", SqlDbType.Int) }); + rec.SetInt32(0, x); + return rec; + }) + }; + + var parameters = new List() { sqlParam }; + const string SQL = @"SELECT * FROM information_schema.COLUMNS cols INNER JOIN @ids Ids on Ids.id = cols.ORDINAL_POSITION"; + using SqlCommand cmd = new(SQL, connection); + cmd.CommandTimeout = 100; + AddCommandParameters(cmd, parameters); + new SqlDataAdapter(cmd).Fill(new("BadFunc")); + Assert.False(true, "Expected exception did not occur"); + } + catch (Exception e) + { + // Ignore this exception as it's deliberately introduced. + Assert.True(e.Message.Contains("Object reference not set to an instance of an object"), "Expected exception did not occur"); + } + + // Good Scenario - No failure expected. + try + { + const string SQL = @"SELECT * FROM information_schema.tables WHERE TABLE_NAME = @TableName"; + var parameters = new List() { new SqlParameter("@TableName", "Temp") }; + using SqlCommand cmd = new(SQL, connection); + cmd.CommandTimeout = 100; + AddCommandParameters(cmd, parameters); + new SqlDataAdapter(cmd).Fill(new("GoodFunc")); + } + catch (Exception e) + { + Assert.False(true, $"Unexpected error occurred: {e.Message}"); + } + } + + private class Item + { + public Item(int? v) + { + id = v; + } + public int? id { get; set; } + } + + static internal void AddCommandParameters(SqlCommand command, IEnumerable parameters) + { + if (parameters == null) + return; + + foreach (SqlParameter p in parameters) + { + if (p == null) + continue; + + if (p.Value == null) + { + var clone = (SqlParameter)((ICloneable)p).Clone(); + clone.Value = DBNull.Value; + command.Parameters.Add(clone); + } + else + { + command.Parameters.Add(p); + } + } + } + public TvpTest() { _connStr = DataTestUtility.TCPConnectionString; @@ -693,7 +787,7 @@ private bool AllowableDifference(byte[] source, object result, StePermutation me // allowable max-length adjustments if (maxLength == resultBytes.Length) { // a bit optimistic, but what the heck. - // truncation + // truncation if (maxLength <= source.Length) { returnValue = true; From b479938c9a1c82ea32ae8ea20583a0d6bd47354e Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Tue, 18 May 2021 03:03:42 +0000 Subject: [PATCH 38/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.es.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.fr.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.it.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.ja.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.ko.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.pt-BR.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.ru.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.zh-Hans.resx | 33 ++++++++++--------- .../netfx/src/Resources/Strings.zh-Hant.resx | 33 ++++++++++--------- 10 files changed, 180 insertions(+), 150 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index cb86d1f8f0..4bf7a053c0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -2503,7 +2503,7 @@ "Authentication=Active Directory Device Code Flow" kann nicht mit den Schlüsselwörtern "User ID", "UID", "Password" oder "PWD" für die Verbindungszeichenfolge verwendet werden. - "Authentication={0}" kann nicht mit den Schlüsselwörtern "Password" oder "PWD" für die Verbindungszeichenfolge verwendet werden. + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. "Authentication=Active Directory Integrated" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. @@ -4345,16 +4345,16 @@ Verwendete {0}-Instanz unterstützt die Spaltenverschlüsselung nicht. - Sie haben die Enclave-Nachweis-URL und das Nachweisprotokoll in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine auf Enclave basierenden Berechnungen. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Sie haben die Enclave-Nachweis-URL in der Verbindungszeichenfolge angegeben, aber die SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Sie haben das Nachweisprotokoll in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine auf Enclave basierenden Berechnungen. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Sie haben die Enclave-Nachweis-URL in der Verbindungszeichenfolge angegeben, aber die SQL Server-Instanz hat keinen Enclave-Typ zurückgegeben. Stellen Sie sicher, dass der Enclave-Typ ordnungsgemäß in Ihrer Instanz konfiguriert wurde. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. {0} muss beim Durchführen von Batchupdates für alle Befehle ({1}, {2}, {3}, {4}) identisch sein. @@ -4459,7 +4459,7 @@ Das Ausführungstimeout ist abgelaufen. Der Timeoutzeitraum wurde überschritten, bevor der Vorgang beendet wurde, oder der Server antwortet nicht. - Fehler bei der Überprüfung eines Nachweistokens. Die Tokensignatur stimmt nicht mit der Signatur überein, die anhand eines öffentlichen Schlüssels berechnet wurde, der vom Endpunkt für öffentliche Nachweisschlüssel unter "{0}" abgerufen wurde. Überprüfen Sie die DNS-Zuordnung für den Endpunkt. Wenn diese korrekt ist, wenden Sie sich an den Kundensupport. + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. Interner Fehler beim erneuten Download des HGS-Stammzertifikats, nachdem bei der ersten Anforderung ein Fehler aufgetreten ist. Wenden Sie sich an den Kundensupport. @@ -4480,10 +4480,10 @@ Fehler bei der Überprüfung eines Nachweistokens. Das Token weisen ein ungültiges Format auf. Wenden Sie sich an den Kundensupport. Fehlerdetails: {0}. - Der Nachweisdienst hat ein abgelaufenes HGS-Stammzertifikat für die Nachweis-URL {0} zurückgegeben. Überprüfen Sie das für Ihre HGS-Instanz konfigurierte HGS-Stammzertifikat. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Das abgerufene HGS-Stammzertifikat für die Nachweis-URL {0} weist ein ungültiges Format auf. Überprüfen Sie, ob die Nachweis-URL korrekt und der HGS-Server online und vollständig initialisiert ist. Wenden Sie sich an den Kundensupport, um weitere Informationen zu erhalten. Fehlerdetails: "{1}". + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. Fehler bei der Überprüfung eines Nachweistokens. Ein öffentlicher Schlüssel kann nicht aus dem Endpunkt für öffentliche Nachweisschlüssel abgerufen werden, oder der abgerufene Schlüssel weist ein ungültiges Format auf. Fehlerdetails: {0}. @@ -4501,29 +4501,32 @@ Fehler bei der Überprüfung des Nachweistokens während der Signaturüberprüfung. Ausnahme: {0}. - Fehler bei der Überprüfung eines Nachweistokens. Der Anspruch "{0}" im Token weist den ungültigen Wert "{1}" auf. Überprüfen Sie die Nachweisrichtlinie. Wenn die Richtlinie korrekt ist, wenden Sie sich an den Kundensupport. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - Fehler bei der Überprüfung des Nachweistokens. Der Anspruch "{0}" fehlt im Token. Überprüfen Sie die Nachweisrichtlinie. Wenn die Richtlinie korrekt ist, wenden Sie sich an den Kundensupport. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. Es konnte nicht überprüft werden, ob die Enclave im Produktionsmodus ausgeführt wird. Wenden Sie sich an den Kundensupport. - Die Enclaverichtlinie konnte aufgrund einer Abweichung zwischen den erwarteten und den tatsächlichen Werten der Richtlinie für die Eigenschaft "{0}" nicht überprüft werden. Tatsächlich: "{1}", erwartet: "{2}". + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Fehler bei der Signaturüberprüfung des Enclaveberichts. Die Berichtssignatur stimmt nicht mit der Signatur überein, die mit dem HGS-Stammzertifikat berechnet wurde. Überprüfen Sie die DNS-Zuordnung für den Endpunkt. Wenn sie korrekt ist, wenden Sie sich an den Kundensupport. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. Der von SQL Server empfangene Enclavebericht weist nicht das richtige Format auf. Wenden Sie sich an den Kundensupport. - Fehler beim Erstellen einer Vertrauenskette zwischen dem Integritätsbericht für den Enclave-Host und dem HGS-Stammzertifikat für die Nachweis-URL {0} mit dem Status "{1}". Überprüfen Sie, ob die Nachweis-URL mit der auf dem SQL Server-Computer konfigurierten URL übereinstimmt. Wenn sowohl der Client als auch SQL Server denselben Nachweisdienst verwenden, wenden Sie sich an den Kundensupport. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. Gibt ein Nachweisprotokoll für den entsprechenden Enclave-Nachweisdienst an. + + Specifies an IP address preference when connecting to SQL instances. + Der vom Server zurückgegebene Enclave-Typ "{0}" wird nicht unterstützt. @@ -4576,13 +4579,13 @@ Die Eigenschaft "Credential" kann nicht festgelegt werden, wenn in der Verbindungszeichenfolge "Authentication=Active Directory Device Code Flow" angegeben wurde. - Die Eigenschaft "Credential" kann nicht festgelegt werden, wenn in der Verbindungszeichenfolge "Authentication={0}" angegeben wurde. + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. "Authentication=Active Directory Device Code Flow" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. - "Authentication={0}" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. + Cannot use 'Authentication={0}', if the Credential property has been set. Unerwarteter Typ beim Deserialisieren erkannt. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index 1dc17be275..b9ffb149d4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -2503,7 +2503,7 @@ No se puede usar "Authentication=Active Directory Device Code Flow" con las palabras clave de cadena de conexión "User ID", "UID", "Password" ni "PWD". - No se puede usar "Authentication={0}" con las palabras clave de cadena de conexión "Password" ni "PWD". + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. No se puede usar "Authentication=Active Directory Integrated" si se ha establecido la propiedad Credential. @@ -4345,16 +4345,16 @@ La instancia de {0} en uso no admite el cifrado de columnas. - Ha especificado la dirección URL de atestación de enclave y el protocolo de atestación en la cadena de conexión, pero la instancia de SQL Server que se está usando no admite los cálculos basados en enclave. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Ha especificado la dirección URL de atestación de enclave en la cadena de conexión, pero la instancia de SQL Server en uso no admite cálculos basados en enclave. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Ha especificado el protocolo de atestación en la cadena de conexión, pero la instancia de SQL Server que se está usando no admite cálculos basados en enclave. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Ha especificado la dirección URL de atestación de enclave en la cadena de conexión, pero la instancia de SQL Server no devolvió un tipo de enclave. Asegúrese de que el tipo de enclave está configurado correctamente en la instancia. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. {0} debería ser idéntico en todos los comandos ({1}, {2}, {3}, {4}) cuando se ejecutan actualizaciones en lote. @@ -4459,7 +4459,7 @@ Se agotó el tiempo de espera de ejecución. El período de tiempo de espera transcurrió antes de la finalización de la operación o el servidor no responde. - Error en la validación de un token de atestación. La firma del token no coincide con la firma calculada mediante una clave pública recuperada del punto de conexión de clave pública de atestación en "{0}". Compruebe la asignación de DNS del punto de conexión. Si es correcta, póngase en contacto con el servicio de atención al cliente. + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. Se ha producido un error interno al reintentar la descarga del certificado raíz de HGS después de un error en la solicitud inicial. Póngase en contacto con el servicio de atención al cliente. @@ -4480,10 +4480,10 @@ Error en la validación de un token de atestación. El token tiene un formato no válido. Póngase en contacto con el servicio de atención al cliente. Detalles del error: "{0}". - El servicio de atestación devolvió un certificado raíz de HGS expirado para la dirección URL de atestación "{0}". Compruebe el certificado raíz de HGS configurado para su instancia de HGS. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - El certificado raíz de HGS obtenido para la dirección URL de atestación "{0}" tiene un formato no válido. Compruebe que la dirección URL de atestación sea correcta y que el servidor HGS esté en línea y completamente inicializado. Para obtener más información, póngase en contacto con los servicios de atención al cliente. Detalles del error: "{1}". + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. Error en la validación de un token de atestación. No se puede recuperar una clave pública del punto de conexión de clave pública de atestación o la clave recuperada tiene un formato no válido. Detalles del error: "{0}". @@ -4501,29 +4501,32 @@ Error en la validación del token de atestación durante la validación de la firma. Excepción: "{0}". - Error en la validación de un token de atestación. La notificación "{0}" del token tiene un valor no válido de "{1}". Compruebe la directiva de atestación. Si es correcta, póngase en contacto con el servicio de atención al cliente. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - Error en la validación del token de atestación. Falta la notificación "{0}" en el token. Compruebe la directiva de atestación. Si es correcta, póngase en contacto con el servicio de atención al cliente. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. No se pudo comprobar si el enclave se está ejecutando en modo de producción. Póngase en contacto con el servicio de atención al cliente. - No se pudo comprobar la directiva de enclave debido a una diferencia entre los valores reales y esperados de la directiva en la propiedad "{0}". Valores reales: "{1}"; valores esperados: "{2}". + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Error de comprobación de firma del informe de enclave. La firma del informe no coincide con la firma calculada mediante el certificado raíz de HGS. Compruebe la asignación de DNS del punto de conexión. Si es correcta, póngase en contacto con el servicio de atención al cliente. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. El informe de enclave recibido desde SQL Server no tiene el formato correcto. Póngase en contacto con el servicio de atención al cliente. - No se pudo compilar una cadena de confianza entre el informe de mantenimiento del host del enclave y el certificado raíz de HGS para la dirección URL de atestación "{0}" con el estado "{1}". Compruebe que la dirección URL de atestación coincida con la configurada en la máquina SQL Server. Si el cliente y SQL Server usan el mismo servicio de atestación, póngase en contacto con los servicios de atención al cliente. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. Especifica un protocolo de atestación para el servicio de atestación de enclave correspondiente. + + Specifies an IP address preference when connecting to SQL instances. + No se admite el tipo de enclave "{0}" que ha devuelto el servidor. @@ -4576,13 +4579,13 @@ No se puede establecer la propiedad Credential si se ha especificado "Authentication=Active Directory Device Code Flow" en la cadena de conexión. - No se puede establecer la propiedad Credential si se ha especificado "Authentication={0}" en la cadena de conexión. + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. No se puede usar "Active Directory Device Code Flow" si se ha establecido la propiedad Credential. - No se puede usar "Authentication={0}" si se ha establecido la propiedad Credential. + Cannot use 'Authentication={0}', if the Credential property has been set. Se ha detectado un tipo inesperado al realizar la deserialización. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index a1b40c474c..bf30b45196 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -2503,7 +2503,7 @@ Impossible d'utiliser « Authentication=Active Directory Device Code Flow » avec les mots clés de chaîne de connexion « User ID », « UID », « Password » et « PWD ». - Impossible d'utiliser « Authentication={0} » avec les mots clés de chaîne de connexion « Password » ou « PWD ». + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. Impossible d'utiliser 'Authentication=Active Directory Integrated', si la propriété Credential a été définie. @@ -4345,16 +4345,16 @@ L’instance {0} utilisée ne prend pas en charge le chiffrement de colonne. - Vous avez spécifié l'URL d'attestation et le protocole d'attestation de l'enclave dans la chaîne de connexion, alors que l'instance SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Vous avez spécifié l'URL d'attestation d'enclave dans la chaîne de connexion, mais l'instance SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Vous avez spécifié le protocole d'attestation dans la chaîne de connexion, mais l'instance SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Vous avez spécifié l'URL d'attestation d'enclave dans la chaîne de connexion, mais l'instance SQL Server n'a pas retourné de type d'enclave. Vérifiez que le type d'enclave est correctement configuré dans votre instance. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. {0} doit être identique dans toutes les commandes ({1}, {2}, {3}, {4}) pendant la réalisation de mises à jour par lot. @@ -4459,7 +4459,7 @@ Le délai d'exécution a expiré. Le délai d'attente s'est écoulé avant la fin de l'opération ou le serveur ne répond pas. - La validation d'un jeton d'attestation a échoué. La signature du jeton ne correspond pas à la signature calculée à l'aide d'une clé publique récupérée sur le point de terminaison de clé publique d'attestation dans « {0} ». Vérifiez le mappage DNS pour le point de terminaison. S'il est correct, contactez le service client. + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. Une erreur interne s'est produite pendant la nouvelle tentative de téléchargement du certificat racine SGH après l'échec de la demande initiale. Contactez le service client. @@ -4480,10 +4480,10 @@ La validation d'un jeton d'attestation a échoué. Le jeton a un format non valide. Contactez le service client. Détails de l'erreur : « {0} ». - Le service d'attestation a retourné un certificat racine SGH expiré pour l'URL d'attestation « {0} ». Vérifiez le certificat racine SGH configuré pour votre instance SGH. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Le certificat racine SGH obtenu pour l'URL d'attestation « {0} » a un format non valide. Vérifiez que l'URL d'attestation est correcte et que le serveur SGH est en ligne et complètement initialisé. Pour plus d'informations, contactez le service client. Détails de l'erreur : « {1} ». + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. La validation d'un jeton d'attestation a échoué. Impossible de récupérer une clé publique sur le point de terminaison de clé publique d'attestation, ou la clé récupérée a un format non valide. Détails de l'erreur : « {0} ». @@ -4501,29 +4501,32 @@ La validation du jeton d'attestation a échoué pendant la validation de la signature. Exception : « {0} ». - La validation d'un jeton d'attestation a échoué. La revendication « {0} » dans le jeton a la valeur non valide « {1} ». Vérifiez la stratégie d'attestation. Si la stratégie est correcte, contactez le service client. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - La validation du jeton d'attestation a échoué. La revendication « {0} » est manquante dans le jeton. Vérifiez la stratégie d'attestation. Si la stratégie est correcte, contactez le service client. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. La vérification de l'exécution de l'enclave en mode de production a échoué. Contactez le service client. - Impossible de vérifier la stratégie de l'enclave en raison d'une différence entre la valeur attendue et la valeur réelle de la stratégie sur la propriété '{0}'. Valeur réelle : '{1}', valeur attendue : '{2}'. + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - La vérification de signature du rapport d'enclave a échoué. La signature de rapport ne correspond pas à la signature calculée à l'aide du certificat racine SGH. Vérifiez le mappage DNS pour le point de terminaison. S'il est correct, contactez le service client. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. Le rapport d'enclave envoyé par SQL Server n'a pas un format correct. Contactez le service client. - La génération d'une chaîne d'approbation entre le rapport d'intégrité de l'hôte d'enclave et le certificat racine SGH pour l'URL d'attestation « {0} » a échoué avec l'état « {1} ». Vérifiez que l'URL d'attestation correspond à l'URL configurée sur la machine SQL Server. Si le client et le serveur SQL utilisent le même service d'attestation, contactez le service client. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. Spécifie un protocole d'attestation pour le service d'attestation d'enclave correspondant. + + Specifies an IP address preference when connecting to SQL instances. + Le type d'enclave « {0} » retourné par le serveur n'est pas pris en charge. @@ -4576,13 +4579,13 @@ Impossible de définir la propriété Credential si « Authentication=Active Directory Device Code Flow » est spécifié dans la chaîne de connexion. - Impossible de définir la propriété Credential si « Authentication={0} » a été spécifié dans la chaîne de connexion. + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Impossible d'utiliser « Authentication=Active Directory Device Code Flow » si la propriété Credential est définie. - Impossible d'utiliser « Authentication={0} », si la propriété Credential a été définie. + Cannot use 'Authentication={0}', if the Credential property has been set. Type inattendu détecté pendant la désérialisation. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 9b3806809c..efdc210b0f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -2503,7 +2503,7 @@ Non è possibile usare 'Authentication=Active Directory Device Code Flow' con le parole chiave della stringa di connessione 'User ID', 'UID', 'Password' o 'PWD'. - Non è possibile usare 'Authentication={0}' con le parole chiave della stringa di connessione 'Password' o 'PWD'. + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. Non è possibile usare 'Authentication=Active Directory Integrated' se è stata impostata la proprietà Credential. @@ -4345,16 +4345,16 @@ L'istanza {0} in uso non supporta la crittografia di colonna. - Sono stati specificati l'URL di attestazione dell'enclave e il protocollo di attestazione nella stringa di connessione, ma l'istanza di SQL Server in uso non supporta i calcoli basati su enclave. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - L'URL di attestazione dell'enclave è stato specificato nella stringa di connessione, ma l'istanza di SQL Server non supporta i calcoli basati su enclave. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - È stato specificato il protocollo di attestazione nella stringa di connessione, ma l'istanza di SQL Server in uso non supporta i calcoli basati su enclave. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - L'URL di attestazione dell'enclave è stato specificato nella stringa di connessione, ma l'istanza di SQL Server non ha restituito un tipo di enclave. Assicurarsi che il tipo di enclave sia configurato correttamente nell'istanza. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. Quando si eseguono aggiornamenti in batch, {0} deve essere identico in tutti i comandi ({1}, {2}, {3}, {4}). @@ -4459,7 +4459,7 @@ Il timeout di esecuzione è scaduto. Il periodo di timeout è scaduto prima del completamento dell'operazione oppure il server non risponde. - La convalida di un token di attestazione non è riuscita. La firma del token non corrisponde alla firma calcolata usando una chiave pubblica recuperata dall'endpoint della chiave pubblica di attestazione in '{0}'. Verificare il mapping DNS per l'endpoint. Se è corretto, contattare il Servizio Supporto Tecnico Clienti. + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. Si è verificato un errore interno durante il tentativo di eseguire il download del certificato radice HGS dopo che la richiesta iniziale non è riuscita. Contattare il Servizio Supporto Tecnico Clienti. @@ -4480,10 +4480,10 @@ La convalida di un token di attestazione non è riuscita. Il formato del token non è valido. Contattare il Servizio Supporto Tecnico Clienti. Dettagli dell'errore: '{0}'. - Il servizio di attestazione ha restituito un certificato radice HGS scaduto per l'URL di attestazione '{0}'. Controllare il certificato radice HGS configurato per l'istanza di HGS. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Il formato del certificato radice HGS ottenuto per l'URL di attestazione '{0}' non è valido. Verificare che l'URL di attestazione sia corretto e che il server HGS sia online e completamente inizializzato. Per altre informazioni, contattare il Servizio Supporto Tecnico Clienti. Dettagli errore: '{1}'. + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. La convalida di un token di attestazione non è riuscita. Non è possibile recuperare una chiave pubblica dall'endpoint della chiave pubblica di attestazione oppure la chiave recuperata ha un formato non valido. Dettagli dell'errore: '{0}'. @@ -4501,29 +4501,32 @@ La convalida del token di attestazione non è riuscita durante la convalida della firma. Eccezione: '{0}'. - La convalida di un token di attestazione non è riuscita. Il valore '{1}' dell'attestazione '{0}' nel token non è valido. Verificare i criteri di attestazione. Se i criteri sono corretti, contattare il Servizio Supporto Tecnico Clienti. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - La convalida del token di attestazione non è riuscita. L'attestazione '{0}' non è presente nel token. Verificare i criteri di attestazione. Se i criteri sono corretti, contattare il Servizio Supporto Tecnico Clienti. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. Non è stato possibile verificare se l'enclave è in esecuzione in modalità di produzione. Contattare il Servizio Supporto Tecnico Clienti. - Non è stato possibile verificare i criteri dell'enclave a causa di una differenza tra il valore previsto e il valore effettivo dei criteri per la proprietà '{0}'. Effettivo: '{1}', previsto: '{2}'. + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - La verifica della firma del report dell'enclave non è riuscita. La firma del report non corrisponde alla firma calcolata usando il certificato radice HGS. Verificare il mapping DNS per l'endpoint. Se è corretto, contattare il Servizio Supporto Tecnico Clienti. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. Il formato del report dell'enclave ricevuto da SQL Server non è corretto. Contattare il Servizio Supporto Tecnico Clienti. - Non è stato possibile creare una catena di certificati tra il report sull'integrità dell'host dell'enclave e il certificato radice HGS per l'URL di attestazione '{0}' con stato: '{1}'. Verificare che l'URL di attestazione corrisponda all'URL configurato nel computer SQL Server. Se il client e SQL Server usano lo stesso servizio di attestazione, contattare il Servizio Supporto Tecnico Clienti. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. Specifica un protocollo di attestazione per il servizio di attestazione dell'enclave corrispondente. + + Specifies an IP address preference when connecting to SQL instances. + Il tipo di enclave '{0}' restituito dal server non è supportato. @@ -4576,13 +4579,13 @@ Non è possibile impostare la proprietà Credential se nella stringa di connessione è stato specificato 'Authentication=Active Directory Device Code Flow'. - Non è possibile impostare la proprietà Credential se nella stringa di connessione è stato specificato 'Authentication={0}'. + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Non è possibile usare 'Authentication=Active Directory Device Code Flow' se è stata impostata la proprietà Credential. - Non è possibile usare 'Authentication={0}' se è stata impostata la proprietà Credential. + Cannot use 'Authentication={0}', if the Credential property has been set. Tipo imprevisto rilevato durante la deserializzazione. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index e0c2159d01..7003299d5c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -2503,7 +2503,7 @@ 'Authentication=Active Directory Device Code Flow' と接続文字列キーワード 'User ID'、'UID'、'Password'、'PWD' を一緒に使用することはできません。 - 'Authentication={0}' と接続文字列キーワード 'Password'、'PWD' を一緒に使用することはできません。 + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. Credential プロパティが設定されている場合は、'Authentication=Active Directory Integrated' を使用できません。 @@ -4345,16 +4345,16 @@ 使用中の {0} インスタンスは列暗号化をサポートしていません。 - 接続文字列の中でエンクレーブ構成証明の URL と構成証明プロトコルが指定されていますが、使用中の SQL Server インスタンスではエンクレーブに基づく計算はサポートされていません。 + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 接続文字列の中でエンクレーブ構成証明 URL が指定されていますが、使用中の SQL Server インスタンスではエンクレーブに基づく計算がサポートされていません。 + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 接続文字列の中で構成証明プロトコルが指定されていますが、使用中の SQL Server インスタンスではエンクレーブに基づく計算はサポートされていません。 + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 接続文字列の中でエンクレーブ構成証明 URL が指定されていますが、SQL Server インスタンスからはエンクレーブ型が返されませんでした。お使いのインスタンスでエンクレーブ型が正しく構成されていることをご確認ください。 + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. バッチ更新を実行する際は、{0} を全コマンド ({1}、{2}、{3}、{4}) 上で一致させる必要があります。 @@ -4459,7 +4459,7 @@ 実行タイムアウトの期限が切れました。操作完了前にタイムアウト期間が過ぎたか、サーバーが応答していません。 - 構成証明トークンの検証に失敗しました。トークンの署名が、'{0}' の構成証明の公開キー エンドポイントから取得した公開キーを使用して計算された署名と一致しません。エンドポイントの DNS マッピングをご確認ください。適切である場合、カスタマー サポート サービスにお問い合わせください。 + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. 最初の要求が失敗した後に、HGS ルート証明書のダウンロードを再試行しているときに内部エラーが発生しました。カスタマー サポート サービスにお問い合わせください。 @@ -4480,10 +4480,10 @@ 構成証明トークンの検証に失敗しました。トークンの形式が無効です。カスタマー サポート サービスにお問い合わせください。エラーの詳細: '{0}'。 - 構成証明サービスにより、構成証明 URL '{0}' の期限切れの HGS ルート証明書が返されました。HGS インスタンス用に構成されている HGS ルート証明書をご確認ください。 + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - 構成証明 URL '{0}' に関する、取得された HGS ルート証明書の形式が無効です。構成証明 URL が正しいこと、および HGS サーバーがオンラインであり完全に初期化されていることをご確認ください。詳細については、カスタマー サポート サービスにお問い合わせください。エラーの詳細: '{1}'。 + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. 構成証明トークンの検証に失敗しました。構成証明の公開キー エンドポイントから公開キーを取得できないか、取得したキーの形式が無効です。エラーの詳細: '{0}'。 @@ -4501,29 +4501,32 @@ 署名の検証中に、構成証明トークンの検証に失敗しました。例外: '{0}'。 - 構成証明トークンの検証に失敗しました。トークン内の要求 '{0}' の値 '{1}' が無効です。構成証明ポリシーをご確認ください。ポリシーが適切である場合、カスタマー サポート サービスにお問い合わせください。 + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - 構成証明トークンの検証に失敗しました。要求 '{0}' がトークンにありません。構成証明ポリシーをご確認ください。ポリシーが適切である場合、カスタマー サポート サービスにお問い合わせください。 + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. エンクレーブが運用モードで実行されているかどうかを確認できませんでした。カスタマー サポート サービスにお問い合わせください。 - プロパティ '{0}' のポリシーの必要な値と実際の値の違いにより、エンクレーブ ポリシーを確認できませんでした。実際の値: '{1}'、必要な値: '{2}'。 + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - エンクレーブ レポートの署名の検証に失敗しました。レポートの署名が、HGS ルート証明書を使用して計算された署名と一致しません。エンドポイントの DNS マッピングをご確認ください。適切である場合、カスタマー サポート サービスにお問い合わせください。 + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. SQL Server から受信したエンクレーブ レポートの形式が適切ではありません。カスタマー サポート サービスにお問い合わせください。 - エンクレーブ ホストの正常性レポートと構成証明 URL '{0}' の HGS ルート証明書との間で信頼チェーンを構築できませんでした。状態は '{1}' です。構成証明 URL が SQL Server マシンで構成されている URL と一致していることをご確認ください。クライアントと SQL Server の両方で同じ構成証明サービスが使用されている場合は、カスタマー サポート サービスにお問い合わせください。 + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. 対応するエンクレーブ構成証明サービスに対して構成証明プロトコルを指定します。 + + Specifies an IP address preference when connecting to SQL instances. + サーバーから返されたエンクレーブの種類 '{0}' はサポートされていません。 @@ -4576,13 +4579,13 @@ 接続文字列で 'Authentication=Active Directory Device Code Flow' が指定されている場合は、Credential プロパティを設定できません。 - 接続文字列で 'Authentication={0}' が指定されている場合は、Credential プロパティを設定できません。 + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Credential プロパティが設定されている場合は、'Authentication=Active Directory Device Code Flow' を使用できません。 - Credential プロパティが設定されている場合は、'Authentication={0}' を使用できません。 + Cannot use 'Authentication={0}', if the Credential property has been set. 逆シリアル化で予期しない型が検出されました。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 8c779955d5..97580a10df 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -2503,7 +2503,7 @@ 'Authentication=Active Directory Device Code Flow'를 'User ID', 'UID', 'Password' 또는 'PWD' 연결 문자열 키워드와 함께 사용할 수 없습니다. - 'Authentication={0}'을(를) 'Password' 또는 'PWD' 연결 문자열 키워드와 함께 사용할 수 없습니다. + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. Credential 속성이 설정된 경우 'Authentication=Active Directory Integrated'를 사용할 수 없습니다. @@ -4345,16 +4345,16 @@ 사용 중인 {0} 인스턴스에서 열 암호화를 지원하지 않습니다. - 연결 문자열에 enclave 증명 URL 및 증명 프로토콜을 지정했지만 사용 중인 SQL Server 인스턴스가 enclave 기반 계산을 지원하지 않습니다. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 연결 문자열에 enclave 증명 URL을 지정했지만 사용 중인 SQL Server 인스턴스가 enclave 기반 계산을 지원하지 않습니다. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 연결 문자열에 증명 프로토콜을 지정했지만 사용 중인 SQL Server 인스턴스가 enclave 기반 계산을 지원하지 않습니다. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 연결 문자열에 enclave 증명 URL을 지정했지만 SQL Server 인스턴스는 enclave 형식을 반환하지 않았습니다. enclave 형식이 인스턴스에 올바르게 구성되어 있는지 확인하세요. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. 일괄 업데이트를 수행할 때 {0}이(가) 모든 명령({1}, {2}, {3}, {4})에서 동일해야 합니다. @@ -4459,7 +4459,7 @@ 실행 제한 시간을 초과했습니다. 작업이 완료되기 전에 실행 제한 시간이 지났거나 서버가 응답하지 않습니다. - 증명 토큰의 유효성을 검사하지 못했습니다. 토큰 서명이 '{0}'의 증명 공개 키 엔드포인트에서 검색된 공개 키를 사용하여 계산된 서명과 일치하지 않습니다. 엔드포인트에 대한 DNS 매핑을 확인하세요. 올바른 경우 고객 지원 서비스에 문의하세요. + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. 초기 요청이 실패한 후 HGS 루트 인증서 다운로드를 다시 시도하는 동안 내부 오류가 발생했습니다. 고객 지원 서비스에 문의하세요. @@ -4480,10 +4480,10 @@ 증명 토큰의 유효성을 검사하지 못했습니다. 토큰의 형식이 유효하지 않습니다. 고객 지원 서비스에 문의하세요. 오류 세부 정보: '{0}'. - 증명 서비스가 증명 URL '{0}'에 대해 만료된 HGS 루트 인증서를 반환했습니다. HGS 인스턴스에 대해 구성된 HGS 루트 인증서를 확인하세요. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - 증명 URL '{0}'에 대해 가져온 HGS 루트 인증서의 형식이 유효하지 않습니다. 증명 URL이 올바르며 HGS 서버가 온라인 상태이고 완전히 초기화되었는지 확인하세요. 자세한 내용은 고객 지원 서비스 팀에 문의하세요. 오류 세부 정보: '{1}'. + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. 증명 토큰의 유효성을 검사하지 못했습니다. 증명 공개 키 엔드포인트에서 공개 키를 검색할 수 없거나, 검색된 키의 형식이 유효하지 않습니다. 오류 세부 정보: '{0}'. @@ -4501,29 +4501,32 @@ 서명 유효성 검사 도중 증명 토큰의 유효성을 검사하지 못했습니다. 예외: '{0}'. - 증명 토큰의 유효성을 검사하지 못했습니다. 토큰의 클레임 '{0}'에 유효하지 않은 값 '{1}'이(가) 있습니다. 증명 정책을 확인하세요. 정책이 올바르면 고객 지원 서비스에 문의하세요. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - 증명 토큰의 유효성을 검사하지 못했습니다. 클레임 '{0}'이(가) 토큰에 없습니다. 증명 정책을 확인하세요. 정책이 올바르면 고객 지원 서비스에 문의하세요. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. enclave가 프로덕션 모드에서 실행되고 있는지 확인하지 못했습니다. 고객 지원 서비스에 문의하세요. - 속성 '{0}'의 예상 값과 실제 값 간의 차이로 인해 enclave 정책을 확인할 수 없습니다. 실제: '{1}', 예상: '{2}'. + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - enclave 보고서의 서명을 확인하지 못했습니다. 보고서 서명이 HGS 루트 인증서를 사용하여 계산된 서명과 일치하지 않습니다. 엔드포인트에 대한 DNS 매핑을 확인하세요. 올바른 경우 고객 지원 서비스에 문의하세요. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. SQL Server에서 수신된 enclave 보고서의 형식이 잘못되었습니다. 고객 지원 서비스에 문의하세요. - 상태가 '{1}' 인 증명 URL '{0}'에 대한 HGS 루트 인증서와 enclave 호스트의 상태 보고서 간에 신뢰 체인을 만들지 못했습니다. 증명 URL이 SQL Server 컴퓨터에 구성된 URL과 일치하는지 확인하세요. 클라이언트와 SQL Server 모두 동일한 증명 서비스를 사용하는 경우 고객 지원 서비스 팀에 문의하세요. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. 해당하는 enclave 증명 서비스에 대한 증명 프로토콜을 지정합니다. + + Specifies an IP address preference when connecting to SQL instances. + 서버에서 반환된 enclave 형식 '{0}'은(는) 지원되지 않습니다. @@ -4576,13 +4579,13 @@ 'Authentication=Active Directory Device Code Flow'가 연결 문자열에 지정된 경우 Credential 속성을 설정할 수 없습니다. - 'Authentication={0}'이(가) 연결 문자열에 지정된 경우 자격 증명 속성을 설정할 수 없습니다. + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Credential 속성이 설정된 경우 'Authentication=Active Directory Device Code Flow'를 사용할 수 없습니다. - 자격 증명 속성이 설정된 경우 'Authentication={0}'을(를) 사용할 수 없습니다. + Cannot use 'Authentication={0}', if the Credential property has been set. 역직렬화 시 예기치 않은 형식이 검색되었습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index e5a0b36503..be8e53867c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -2503,7 +2503,7 @@ Não é possível usar 'Authentication=Active Directory Device Code Flow' com as palavras-chave de cadeia de conexão 'User ID', 'UID', 'Password' ou 'PWD'. - Não é possível usar 'Authentication={0}' com as palavras-chave de cadeia de conexão 'Password' ou 'PWD'. + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. Não é possível usar 'Authentication=Active Directory Integrated' quando a propriedade Credential está definida. @@ -4345,16 +4345,16 @@ A instância {0} não dá suporte à criptografia de coluna. - Você especificou a URL de atestado e o protocolo de atestado de enclave na cadeia de conexão, mas a instância do SQL Server em uso não dá suporte a computações baseadas em enclave. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Você especificou a URL de atestado do enclave na cadeia de conexão, mas a instância do SQL Server não dá suporte para cálculos baseados em enclave. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Você especificou o protocolo de atestado na cadeia de conexão, mas a instância do SQL Server em uso não dá suporte a computações baseadas em enclave. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Você especificou a URL de atestado do enclave na cadeia de conexão, mas a instância do SQL Server não retornou um tipo de enclave. Certifique-se de que o tipo de enclave esteja configurado corretamente na sua instância. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. {0} deve corresponder em todos os comandos ({1}, {2}, {3}, {4}) nas atualizações em lote. @@ -4459,7 +4459,7 @@ Tempo Limite de Execução Expirado. O período de tempo limite terminou antes da conclusão da operação ou o servidor não está respondendo. - Falha na validação de um token de atestado. A assinatura do token não corresponde à assinatura computada usando uma chave pública recuperada do ponto de extremidade de chave pública do atestado em '{0}'. Verifique o mapeamento DNS do ponto de extremidade. Se ele estiver correto, entre em contato com os Serviços de Atendimento ao Cliente. + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. Ocorreu um erro interno ao repetir o download do certificado raiz HGS após a falha da solicitação inicial. Entre em contato com os Serviços de Atendimento ao Cliente. @@ -4480,10 +4480,10 @@ A validação de um token de atestado falhou. O token tem um formato inválido. Entre em contato com os Serviços de Atendimento ao Cliente. Detalhes do erro: '{0}'. - O serviço de atestado retornou um certificado raiz HGS expirado para a URL de atestado '{0}'. Verifique o certificado raiz HGS configurado para a instância de HGS. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - O certificado raiz de HGS obtido para a URL de atestado '{0}' tem um formato inválido. Verifique se a URL de atestado está correta e se o servidor HGS está online e totalmente inicializado. Para obter mais informações, entre em contato com os Serviços de Atendimento ao Cliente. Detalhes do erro: '{1}'. + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. A validação de um token de atestado falhou. Não é possível recuperar uma chave pública do ponto de extremidade de chave pública de atestado ou a chave recuperada tem um formato inválido. Detalhes do erro: '{0}'. @@ -4501,29 +4501,32 @@ A validação do token de atestado falhou durante a validação de assinatura. Exceção: '{0}'. - A validação de um token de atestado falhou. A declaração '{0}' no token tem um valor inválido de '{1}'. Verifique a política de atestado. Se a política estiver correta, entre em contato com os Serviços de Atendimento ao Cliente. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - A validação do token de atestado falhou. A declaração '{0}' está ausente no token. Verifique a política de atestado. Se a política estiver correta, entre em contato com os Serviços de Atendimento ao Cliente. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. Falha ao verificar se o enclave está sendo executado no modo de produção. Entre em contato com os Serviços de Atendimento ao Cliente. - Não foi possível verificar a política de enclave devido a uma diferença entre os valores esperados e reais da política na propriedade '{0}'. Real: '{1}', Esperado: '{2}'. + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Falha na verificação da assinatura do relatório de enclave. A assinatura do relatório não corresponde à assinatura calculada usando o certificado raiz HGS. Verifique o mapeamento DNS do ponto de extremidade. Se correto, entre em contato com os Serviços de Atendimento ao Cliente. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. O relatório de enclave recebido do SQL Server não está no formato correto. Entre em contato com os Serviços de Atendimento ao Cliente. - Falha ao criar uma cadeia de confiança entre o relatório de integridade do host de enclave e o certificado raiz HGS para a URL de atestado '{0}' com o status: '{1}'. Verifique se a URL de atestado corresponde à URL configurada no computador do SQL Server. Se o cliente e o SQL Server usam o mesmo serviço de atestado, entre em contato com os Serviços de Atendimento ao Cliente. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. Especifica um protocolo de atestado para o serviço de atestado de enclave correspondente. + + Specifies an IP address preference when connecting to SQL instances. + Não há suporte para o tipo enclave '{0}' retornado do servidor. @@ -4576,13 +4579,13 @@ Não é possível definir a propriedade Credential quando 'Authentication=Active Directory Device Code Flow' está especificado na cadeia de conexão. - Não é possível definir a propriedade Credential quando 'Authentication={0}' é especificado na cadeia de conexão. + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Não é possível usar 'Authentication=Active Directory Device Code Flow' quando a propriedade Credential está configurada. - Não é possível usar 'Authentication={0}' quando a propriedade Credential está configurada. + Cannot use 'Authentication={0}', if the Credential property has been set. Tipo inesperado detectado na desserialização. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 73a96370f8..4cda4f816b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -2503,7 +2503,7 @@ Невозможно использовать "Authentication=Active Directory Device Code Flow" с ключевыми словами строки подключения "User ID", "UID", "Password" или "PWD". - Невозможно использовать "Authentication={0}" с ключевыми словами Password или PWD в строке подключения. + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. Не удается использовать "Authentication=Active Directory Integrated", если задано свойство "Credential". @@ -4345,16 +4345,16 @@ Используемый экземпляр {0} не поддерживает шифрование столбцов. - Вы указали в строке подключения URL-адрес и протокол аттестации анклава, но используемый экземпляр SQL Server не поддерживает вычисления на основе анклава. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - В строке подключения указан URL-адрес аттестации анклава, но используемый экземпляр SQL Server не поддерживает вычисления на основе анклава. + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - Вы указали в строке подключения протокол аттестации, но используемый экземпляр SQL Server не поддерживает вычисления на основе анклава. + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - В строке подключения указан URL-адрес аттестации анклава, но экземпляр SQL Server не вернул тип анклава. Убедитесь, что тип анклава правильно настроен в вашем экземпляре. + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. Параметр {0} должен быть одинаковым для всех команд ({1}, {2}, {3}, {4}) при выполнении пакетных обновлений. @@ -4459,7 +4459,7 @@ Время ожидания выполнения истекло. Время ожидания истекло до завершения операции, или сервер не отвечает. - Проверка токена аттестации не пройдена. Подпись токена не соответствует подписи, вычисленной на базе открытого ключа, полученного от конечной точки открытых ключей аттестации по адресу "{0}". Проверьте сопоставление DNS для конечной точки. Если оно верно, обратитесь в службу поддержки пользователей. + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. Произошла внутренняя ошибка при повторной попытке скачивания корневого сертификата HGS после сбоя начального запроса. Обратитесь в службу поддержки пользователей. @@ -4480,10 +4480,10 @@ Проверка токена аттестации не пройдена. Недопустимый формат токена. Обратитесь в службу поддержки пользователей. Сведения об ошибке: "{0}". - Служба аттестации вернула просроченный корневой сертификат HGS для URL-адреса аттестации "{0}". Проверьте корневой сертификат HGS, настроенный для вашего экземпляра HGS. + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Полученный корневой сертификат HGS для URL-адреса аттестации "{0}" имеет недопустимый формат. Проверьте правильность URL-адреса аттестации и убедитесь, что сервер HGS подключен к сети и полностью инициализирован. За дополнительными сведениями обратитесь в службу поддержки пользователей. Сведения об ошибке: "{1}". + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. Проверка токена аттестации не пройдена. Не удается получить открытый ключ из конечной точки открытого ключа аттестации либо полученный ключ имеет недопустимый формат. Сведения об ошибке: "{0}". @@ -4501,29 +4501,32 @@ Проверка токена аттестации не пройдена при проверке подписи. Исключение: "{0}". - Проверка токена аттестации не пройдена. Недопустимое значение "{1}" утверждения "{0}" в токене. Проверьте политику аттестации. Если политика верна, обратитесь в службу поддержки пользователей. + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - Проверка токена аттестации не пройдена. В токене отсутствует утверждение "{0}". Проверьте политику аттестации. Если политика верна, обратитесь в службу поддержки пользователей. + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. Не удалось проверить, работает ли анклав в рабочем режиме. Обратитесь в службу поддержки пользователей. - Не удалось проверить политику анклава из-за различий между ожидаемым и фактическим значениями политики в свойстве "{0}". Фактическое: "{1}", ожидаемое: "{2}". + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - Проверка подписи отчета анклава не пройдена. Подпись отчета не соответствует подписи, вычисленной с помощью корневого сертификата HGS. Проверьте сопоставление DNS для конечной точки. Если оно верно, обратитесь в службу поддержки пользователей. + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. Полученный с SQL Server отчет анклава имеет неправильный формат. Обратитесь в службу поддержки пользователей. - Не удалось создать цепь доверия между отчетом о работоспособности узла анклава и корневым сертификатом HGS для URL-адреса аттестации "{0}" с состоянием: "{1}". Убедитесь, что URL-адрес аттестации совпадает с URL-адресом, настроенным на компьютере с SQL Server. Если клиент и SQL Server используют одну и ту же службу аттестации, обратитесь в службу поддержки пользователей. + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. Указывает протокол аттестации для соответствующей службы аттестации анклава. + + Specifies an IP address preference when connecting to SQL instances. + Возвращаемый с сервера анклав типа "{0}" не поддерживается. @@ -4576,13 +4579,13 @@ Не удается задать свойство Credential, если в строке подключения указано "Authentication=Active Directory Device Code Flow". - Невозможно задать свойство Credential, если в строке подключения указано "Authentication={0}". + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Невозможно использовать ", если задано свойство Credential. - Невозможно использовать "Authentication={0}", если задано свойство Credential. + Cannot use 'Authentication={0}', if the Credential property has been set. Обнаружен непредвиденный тип при десериализации. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 202dd8dddc..dec4b0070f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -2503,7 +2503,7 @@ 无法结合使用 "Authentication=Active Directory Device Code Flow" 与 "User ID"、"UID"、"Password" 或 "PWD" 连接字符串关键字。 - 无法将 "Authentication={0}" 与 "Password" 或 "PWD" 连接字符串关键字结合使用。 + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. 如果设置了 Credential 属性,则无法使用“Authentication=Active Directory Integrated”。 @@ -4345,16 +4345,16 @@ 使用的 {0} 实例不支持列加密。 - 你已在连接字符串中指定 enclave 证明 URL 和证明协议,但使用中的 SQL Server 实例不支持基于 enclave 的计算。 + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 已在连接字符串中指定 enclave 证明 URL,但正在使用的 SQL Server 实例不支持基于 enclave 的计算。 + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 你已在连接字符串中指定证明协议,但使用中的 SQL Server 实例不支持基于 enclave 的计算。 + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 连接字符串中已指定 enclave 证明 URL,但 SQL Server 实例未返回 enclave 类型。请确保在实例中正确配置 enclave 类型。 + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. 执行批更新时,{0} 的执行方式应与所有命令({1}、{2}、{3}、{4})的执行方式相同。 @@ -4459,7 +4459,7 @@ 执行超时已过期。完成操作之前已超时或服务器未响应。 - 无法验证证明令牌。令牌签名与使用从“{0}”处的证明公钥终结点检索的公钥计算的签名不一致。请验证终结点的 DNS 映射。如果正确,请联系客户支持服务。 + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. 初始请求失败后,重试下载 HGS 根证书时出现内部错误。请联系客户支持服务。 @@ -4480,10 +4480,10 @@ 无法验证证明令牌。令牌的格式无效。请联系客户支持服务。错误详细信息:“{0}”。 - 证明服务为证明 URL“{0}”返回的 HGS 根证书已到期。请检查为你的 HGS 实例配置的 HGS 根证书。 + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - 为证明 URL“{0}”获取的 HGS 根证书的格式无效。请验证证明 URL 是否正确,且 HGS 服务器是否处于联机状态且已完全初始化。有关详细信息,请联系客户支持服务。错误详细信息:“{1}”。 + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. 无法验证证明令牌。无法从证明公钥终结点检索公钥,或检索到的密钥的格式无效。错误详细信息:“{0}”。 @@ -4501,29 +4501,32 @@ 无法在签名验证期间验证证明令牌。异常:“{0}”。 - 无法验证证明令牌。令牌中的声明“{0}”有无效的“{1}” 值。请验证证明策略。如果策略正确,请联系客户支持服务。 + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - 无法验证证明令牌。令牌中缺少声明“{0}”。请验证证明策略。如果策略正确,请联系客户支持服务。 + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. 无法检查 enclave 是否在生产模式下运行。请联系客户支持服务。 - 无法验证 enclave 策略,因为属性“{0}”上策略的预期值和实际值不一致。实际值:“{1}”,预期值:“{2}”。 + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - 无法验证 enclave 报告的签名。报告签名与使用 HGS 根证书计算出的签名不一致。请验证终结点的 DNS 映射。如果正确,请联系客户支持服务。 + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. 从 SQL Server 收到的 enclave 报告的格式不正确。请联系客户支持服务。 - 无法在 enclave 主机的运行状况报告和证明 URL“{0}”的 HGS 根证书之间生成信任链,状态为“{1}”。请验证证明 URL 是否与 SQL Server 计算机上配置的 URL 一致。如果客户端和 SQL Server 使用的证明服务相同,请联系客户支持服务。 + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. 为其相应的 enclave 证明服务指定证明协议。 + + Specifies an IP address preference when connecting to SQL instances. + 不支持从服务器返回的 enclave 类型“{0}”。 @@ -4576,13 +4579,13 @@ 如果在连接字符串中指定了 "Authentication=Active Directory Device Code Flow",则无法设置 Credential 属性。 - 如果在连接字符串中指定了 "Authentication={0}",则无法设置 Credential 属性。 + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. 如果设置了 Credential 属性,则无法使用 "Authentication=Active Directory Device Code Flow"。 - 如果设置了 Credential 属性,则无法使用 "Authentication={0}"。 + Cannot use 'Authentication={0}', if the Credential property has been set. 反序列化时检测到意外的类型。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index f0c30aabc5..35aa363edb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -2503,7 +2503,7 @@ 無法搭配 'User ID'、'UID'、'Password' 或 'PWD' 連接字串關鍵字使用 'Authentication=Active Directory Device Code Flow'。 - 使用 'Authentication={0}' 時,無法搭配 'Password' 或 'PWD' 連接字串關鍵字一起使用。 + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. 如果已設定 Credential 屬性,則無法使用 'Authentication=Active Directory Integrated'。 @@ -4345,16 +4345,16 @@ 使用中的 {0} 執行個體不支援資料行加密。 - 您已在連接字串中指定記憶體保護區證明 URL 和證明通訊協定,但使用中的 SQL Server 執行個體不支援以記憶體保護區為基礎的計算。 + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 您在連接字串中指定了 enclave 證明 URL,但使用的 SQL Server 執行個體不支援 enclave 類型的計算。 + You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 您已在連接字串中指定證明通訊協定,但使用中的 SQL Server 執行個體不支援以記憶體保護區為基礎的計算。 + You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. - 您在連接字串中指定了 enclave 證明 URL,但 SQL Server 執行個體未傳回 enclave 類型。請確定您執行個體中的 enclave 類型設定正確。 + You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. 執行批次更新時,所有命令 ({1}、{2}、{3}、{4}) 中的 {0} 必須一致。 @@ -4459,7 +4459,7 @@ 執行逾時到期。在作業完成之前超過逾時等待的時間,或是伺服器未回應。 - 驗證證明權杖失敗。該權杖簽章與使用從位於 '{0}' 的證明公開金鑰端點擷取而來之公開金鑰計算所得的簽章不符。請檢查該端點的 DNS 對應。若其正確,請連絡客戶支援服務。 + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. 初始要求失敗後,重試下載 HGS 根憑證的下載時發生內部錯誤。請連絡客戶支援服務。 @@ -4480,10 +4480,10 @@ 驗證證明權杖失敗。權杖的格式無效。請連絡客戶支援服務。錯誤詳細資料: '{0}'。 - 證明服務為證明 URL '{0}' 傳回了過期的 HGS 根憑證。請檢查為您 HGS 執行個體設定的 HGS 根憑證。 + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - 為證明 URL '{0}' 取得的 HGS 根憑證格式無效。請確認證明 URL 正確,而且 HGS 伺服器在線上並已完全初始化。如需詳細資訊,請連絡客戶支援服務。錯誤詳細資料: '{1}'。 + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. 驗證證明權杖失敗。無法從證明公開金鑰端點取得公開金鑰,或擷取到了無效格式的金鑰。錯誤詳細資料: '{0}'。 @@ -4501,29 +4501,32 @@ 驗證簽章時,驗證證明權杖失敗。例外狀況: '{0}'。 - 驗證證明權杖失敗。權杖中的宣告 '{0}' 包含無效的 '{1}' 值。請檢查證明原則。若該原則正確,請連絡客戶支援服務。 + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. - 驗證證明權杖失敗。權杖中缺少宣告 '{0}'。請檢查證明原則。若該原則正確,請連絡客戶支援服務。 + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. 無法檢查記憶體保護區是否在生產模式下執行。請連絡客戶支援服務。 - 因為屬性 '{0}' 上原則的應有值與實際值不同,所以無法驗證記憶體保護區原則。實際: '{1}',應為: '{2}'。 + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. - 驗證記憶體保護區報表的簽章失敗。報表的簽章與使用 HGS 根憑證計算所得的簽章不符。請檢查端點的 DNS 對應。若其正確,請連絡客戶支援服務。 + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. 從 SQL Server 收到的記憶體保護區報表的格式不正確。請連絡客戶支援服務。 - 無法在記憶體保護區主機的健康狀態報表與證明 URL '{0}' 的 HGS 根憑證之間建立信任鏈結。狀態: '{1}'。請確認證明 URL 與 SQL Server 電腦上設定的 URL 相符。如果用戶端和 SQL Server 都使用相同的證明服務,請連絡客戶支援服務。 + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. 為證明通訊協定的對應記憶體保護區證明服務指定證明通訊協定。 + + Specifies an IP address preference when connecting to SQL instances. + 不支援從伺服器傳回的記憶體保護區類型 '{0}'。 @@ -4576,13 +4579,13 @@ 如果連接字串中已指定 'Authentication=Active Directory Device Code Flow',則無法設定 Credential 屬性。 - 如果連接字串中已指定 'Authentication={0}',則無法設定 Credential 屬性。 + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. 如果已設定 Credential 屬性,則無法使用 'Authentication=Active Directory Device Code Flow'。 - 如果已設定 Credential 屬性,就無法使用 'Authentication={0}'。 + Cannot use 'Authentication={0}', if the Credential property has been set. 還原序列化時偵測到未預期的類型。 From c6d854a65e1f2fa8c32e3d8cfc0881160ad292bc Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Mon, 17 May 2021 20:42:43 -0700 Subject: [PATCH 39/87] fix debug assert (#1073) --- .../Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs index 1c3f11d049..280655c138 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs @@ -80,12 +80,11 @@ public static SqlRetryLogicBaseProvider CreateFixedRetryProvider(SqlRetryLogicOp private static SqlRetryLogicBaseProvider InternalCreateRetryProvider(SqlRetryLogicOption retryLogicOption, SqlRetryIntervalBaseEnumerator enumerator) { - Debug.Assert(enumerator != null, $"The '{nameof(enumerator)}' mustn't be null."); - if (retryLogicOption == null) { throw new ArgumentNullException(nameof(retryLogicOption)); } + Debug.Assert(enumerator != null, $"The '{nameof(enumerator)}' mustn't be null."); var retryLogic = new SqlRetryLogic(retryLogicOption.NumberOfTries, enumerator, (e) => TransientErrorsCondition(e, retryLogicOption.TransientErrors ?? s_defaultTransientErrors), From 9543d17d96da3aa7773f5dcf28e70b069333ace4 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 18 May 2021 10:23:58 -0700 Subject: [PATCH 40/87] Vulnerability fix | System.Text.Encodings.Web Version override (#1070) --- .../netcore/src/Microsoft.Data.SqlClient.csproj | 1 + .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 +++ tools/props/Versions.props | 1 + 3 files changed, 5 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 00c5c68859..ff1672c097 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -842,6 +842,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 55cbf2500f..a0bf1d5a51 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -581,6 +581,9 @@ + + $(SystemTextEncodingsWebVersion) + $(SystemSecurityCryptographyAlgorithmsVersion) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index db7f049b67..e9f424add2 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -23,6 +23,7 @@ 6.8.0 4.5.1 4.3.0 + 4.7.2 1.0.0 From 6b8a9b745c2251f31b191cce5146501f12de682c Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Wed, 19 May 2021 03:03:54 +0000 Subject: [PATCH 41/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 6 ++-- .../netfx/src/Resources/Strings.es.resx | 6 ++-- .../netfx/src/Resources/Strings.fr.resx | 6 ++-- .../netfx/src/Resources/Strings.it.resx | 32 +++++++++---------- .../netfx/src/Resources/Strings.ja.resx | 6 ++-- .../netfx/src/Resources/Strings.ko.resx | 6 ++-- .../netfx/src/Resources/Strings.pt-BR.resx | 32 +++++++++---------- .../netfx/src/Resources/Strings.ru.resx | 32 +++++++++---------- .../netfx/src/Resources/Strings.zh-Hans.resx | 6 ++-- .../netfx/src/Resources/Strings.zh-Hant.resx | 32 +++++++++---------- 10 files changed, 82 insertions(+), 82 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 4bf7a053c0..f42a330dd1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -2503,7 +2503,7 @@ "Authentication=Active Directory Device Code Flow" kann nicht mit den Schlüsselwörtern "User ID", "UID", "Password" oder "PWD" für die Verbindungszeichenfolge verwendet werden. - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + "Authentication={0}" kann nicht mit den Schlüsselwörtern "Password" oder "PWD" für die Verbindungszeichenfolge verwendet werden. "Authentication=Active Directory Integrated" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. @@ -4579,13 +4579,13 @@ Die Eigenschaft "Credential" kann nicht festgelegt werden, wenn in der Verbindungszeichenfolge "Authentication=Active Directory Device Code Flow" angegeben wurde. - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + Die Eigenschaft "Credential" kann nicht festgelegt werden, wenn in der Verbindungszeichenfolge "Authentication={0}" angegeben wurde. "Authentication=Active Directory Device Code Flow" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. - Cannot use 'Authentication={0}', if the Credential property has been set. + "Authentication={0}" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. Unerwarteter Typ beim Deserialisieren erkannt. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index b9ffb149d4..c46cac7c77 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -2503,7 +2503,7 @@ No se puede usar "Authentication=Active Directory Device Code Flow" con las palabras clave de cadena de conexión "User ID", "UID", "Password" ni "PWD". - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + No se puede usar "Authentication={0}" con las palabras clave de cadena de conexión "Password" ni "PWD". No se puede usar "Authentication=Active Directory Integrated" si se ha establecido la propiedad Credential. @@ -4579,13 +4579,13 @@ No se puede establecer la propiedad Credential si se ha especificado "Authentication=Active Directory Device Code Flow" en la cadena de conexión. - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + No se puede establecer la propiedad Credential si se ha especificado "Authentication={0}" en la cadena de conexión. No se puede usar "Active Directory Device Code Flow" si se ha establecido la propiedad Credential. - Cannot use 'Authentication={0}', if the Credential property has been set. + No se puede usar "Authentication={0}" si se ha establecido la propiedad Credential. Se ha detectado un tipo inesperado al realizar la deserialización. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index bf30b45196..3d44d53917 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -2503,7 +2503,7 @@ Impossible d'utiliser « Authentication=Active Directory Device Code Flow » avec les mots clés de chaîne de connexion « User ID », « UID », « Password » et « PWD ». - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + Impossible d'utiliser « Authentication={0} » avec les mots clés de chaîne de connexion « Password » ou « PWD ». Impossible d'utiliser 'Authentication=Active Directory Integrated', si la propriété Credential a été définie. @@ -4579,13 +4579,13 @@ Impossible de définir la propriété Credential si « Authentication=Active Directory Device Code Flow » est spécifié dans la chaîne de connexion. - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + Impossible de définir la propriété Credential si « Authentication={0} » a été spécifié dans la chaîne de connexion. Impossible d'utiliser « Authentication=Active Directory Device Code Flow » si la propriété Credential est définie. - Cannot use 'Authentication={0}', if the Credential property has been set. + Impossible d'utiliser « Authentication={0} », si la propriété Credential a été définie. Type inattendu détecté pendant la désérialisation. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index efdc210b0f..500eb618bb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -2503,7 +2503,7 @@ Non è possibile usare 'Authentication=Active Directory Device Code Flow' con le parole chiave della stringa di connessione 'User ID', 'UID', 'Password' o 'PWD'. - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + Non è possibile usare 'Authentication={0}' con le parole chiave della stringa di connessione 'Password' o 'PWD'. Non è possibile usare 'Authentication=Active Directory Integrated' se è stata impostata la proprietà Credential. @@ -4345,16 +4345,16 @@ L'istanza {0} in uso non supporta la crittografia di colonna. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Sono stati specificati l'URL di attestazione dell'enclave e il protocollo di attestazione nella stringa di connessione, ma SQL Server in uso non supporta i calcoli basati su enclave - vedere https://go.microsoft.com/fwlink/?linkid=2157649 per altri dettagli. - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + È stato specificato l'URL di attestazione dell'enclave nella stringa di connessione, ma SQL Server in uso non supporta i calcoli basati su enclave - vedere https://go.microsoft.com/fwlink/?linkid=2157649 per altri dettagli. - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + È stato specificato il protocollo di attestazione nella stringa di connessione, ma SQL Server in uso non supporta i calcoli basati su enclave - vedere https://go.microsoft.com/fwlink/?linkid=2157649 per altri dettagli. - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + L'URL di attestazione dell'enclave è stato specificato nella stringa di connessione, ma SQL Server non ha restituito un tipo di enclave. Assicurarsi che il tipo di enclave sia configurato correttamente nell'istanza - vedere https://go.microsoft.com/fwlink/?linkid=2157649 per altri dettagli. Quando si eseguono aggiornamenti in batch, {0} deve essere identico in tutti i comandi ({1}, {2}, {3}, {4}). @@ -4459,7 +4459,7 @@ Il timeout di esecuzione è scaduto. Il periodo di timeout è scaduto prima del completamento dell'operazione oppure il server non risponde. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + La convalida di un token di attestazione non è riuscita. La firma del token non corrisponde alla firma calcolata usando una chiave pubblica recuperata dall'endpoint della chiave pubblica di attestazione in '{0}'. Verificare il mapping DNS per l'endpoint - vedere https://go.microsoft.com/fwlink/?linkid=2157649 per altri dettagli. Se è corretto, contattare il Servizio Supporto Tecnico Clienti. Si è verificato un errore interno durante il tentativo di eseguire il download del certificato radice HGS dopo che la richiesta iniziale non è riuscita. Contattare il Servizio Supporto Tecnico Clienti. @@ -4480,10 +4480,10 @@ La convalida di un token di attestazione non è riuscita. Il formato del token non è valido. Contattare il Servizio Supporto Tecnico Clienti. Dettagli dell'errore: '{0}'. - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Il servizio di attestazione ha restituito un certificato radice HGS scaduto per l'URL di attestazione '{0}'. Verificare il certificato radice HGS configurato per l'istanza di HGS - vedere https://go.microsoft.com/fwlink/?linkid=2160553 per altri dettagli. - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + Il formato del certificato radice HGS ottenuto per l'URL di attestazione '{0}' non è valido. Verificare che l'URL di attestazione sia corretto e che il server HGS sia online e completamente inizializzato - vedere https://go.microsoft.com/fwlink/?linkid=2160553 per altri dettagli. Per ulteriore assistenza, contattare il Servizio Supporto Tecnico Clienti. Dettagli errore: '{1}'. La convalida di un token di attestazione non è riuscita. Non è possibile recuperare una chiave pubblica dall'endpoint della chiave pubblica di attestazione oppure la chiave recuperata ha un formato non valido. Dettagli dell'errore: '{0}'. @@ -4501,31 +4501,31 @@ La convalida del token di attestazione non è riuscita durante la convalida della firma. Eccezione: '{0}'. - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + La convalida del token di attestazione non è riuscita. L'attestazione '{0}' presenta il valore non valido '{1}'. Verificare i criteri di attestazione - vedere https://go.microsoft.com/fwlink/?linkid=2157649 per altri dettagli. Se i criteri sono corretti, contattare il Servizio Supporto Tecnico Clienti. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + La convalida del token di attestazione non è riuscita. L'attestazione '{0}' non è presente nel token. Verificare i criteri di attestazione - vedere https://go.microsoft.com/fwlink/?linkid=2157649 per altri dettagli. Se i criteri sono corretti, contattare il Servizio Supporto Tecnico Clienti. Non è stato possibile verificare se l'enclave è in esecuzione in modalità di produzione. Contattare il Servizio Supporto Tecnico Clienti. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Non è stato possibile verificare i criteri dell'enclave a causa di una differenza tra il valore previsto e il valore effettivo dei criteri per la proprietà '{0}'. Effettivo: '{1}', previsto: '{2}'. - vedere https://go.microsoft.com/fwlink/?linkid=2160553 per altri dettagli. - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + La verifica della firma del report dell'enclave non è riuscita. La firma del report non corrisponde alla firma calcolata usando il certificato radice HGS. Verificare il mapping DNS per l'endpoint - vedere https://go.microsoft.com/fwlink/?linkid=2160553 per altri dettagli. Se è corretto, contattare il Servizio Supporto Tecnico Clienti. Il formato del report dell'enclave ricevuto da SQL Server non è corretto. Contattare il Servizio Supporto Tecnico Clienti. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + Non è stato possibile creare una catena di certificati tra il report sull'integrità dell'host dell'enclave e il certificato radice HGS per l'URL di attestazione '{0}' con stato: '{1}'. Verificare che l'URL di attestazione corrisponda all'URL configurato in SQL Server - vedere https://go.microsoft.com/fwlink/?linkid=2160553 per altri dettagli. Se il client e SQL Server usano lo stesso servizio di attestazione, contattare il Servizio Supporto Tecnico Clienti. Specifica un protocollo di attestazione per il servizio di attestazione dell'enclave corrispondente. - Specifies an IP address preference when connecting to SQL instances. + Specifica una preferenza di indirizzo IP per la connessione alle istanze SQL. Il tipo di enclave '{0}' restituito dal server non è supportato. @@ -4579,13 +4579,13 @@ Non è possibile impostare la proprietà Credential se nella stringa di connessione è stato specificato 'Authentication=Active Directory Device Code Flow'. - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + Non è possibile impostare la proprietà Credential se nella stringa di connessione è stato specificato 'Authentication={0}'. Non è possibile usare 'Authentication=Active Directory Device Code Flow' se è stata impostata la proprietà Credential. - Cannot use 'Authentication={0}', if the Credential property has been set. + Non è possibile usare 'Authentication={0}' se è stata impostata la proprietà Credential. Tipo imprevisto rilevato durante la deserializzazione. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index 7003299d5c..596df25dcd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -2503,7 +2503,7 @@ 'Authentication=Active Directory Device Code Flow' と接続文字列キーワード 'User ID'、'UID'、'Password'、'PWD' を一緒に使用することはできません。 - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + 'Authentication={0}' と接続文字列キーワード 'Password'、'PWD' を一緒に使用することはできません。 Credential プロパティが設定されている場合は、'Authentication=Active Directory Integrated' を使用できません。 @@ -4579,13 +4579,13 @@ 接続文字列で 'Authentication=Active Directory Device Code Flow' が指定されている場合は、Credential プロパティを設定できません。 - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + 接続文字列で 'Authentication={0}' が指定されている場合は、Credential プロパティを設定できません。 Credential プロパティが設定されている場合は、'Authentication=Active Directory Device Code Flow' を使用できません。 - Cannot use 'Authentication={0}', if the Credential property has been set. + Credential プロパティが設定されている場合は、'Authentication={0}' を使用できません。 逆シリアル化で予期しない型が検出されました。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 97580a10df..be6e6e3a08 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -2503,7 +2503,7 @@ 'Authentication=Active Directory Device Code Flow'를 'User ID', 'UID', 'Password' 또는 'PWD' 연결 문자열 키워드와 함께 사용할 수 없습니다. - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + 'Authentication={0}'을(를) 'Password' 또는 'PWD' 연결 문자열 키워드와 함께 사용할 수 없습니다. Credential 속성이 설정된 경우 'Authentication=Active Directory Integrated'를 사용할 수 없습니다. @@ -4579,13 +4579,13 @@ 'Authentication=Active Directory Device Code Flow'가 연결 문자열에 지정된 경우 Credential 속성을 설정할 수 없습니다. - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + 'Authentication={0}'이(가) 연결 문자열에 지정된 경우 자격 증명 속성을 설정할 수 없습니다. Credential 속성이 설정된 경우 'Authentication=Active Directory Device Code Flow'를 사용할 수 없습니다. - Cannot use 'Authentication={0}', if the Credential property has been set. + 자격 증명 속성이 설정된 경우 'Authentication={0}'을(를) 사용할 수 없습니다. 역직렬화 시 예기치 않은 형식이 검색되었습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index be8e53867c..109ae7bec2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -2503,7 +2503,7 @@ Não é possível usar 'Authentication=Active Directory Device Code Flow' com as palavras-chave de cadeia de conexão 'User ID', 'UID', 'Password' ou 'PWD'. - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + Não é possível usar 'Authentication={0}' com as palavras-chave de cadeia de conexão 'Password' ou 'PWD'. Não é possível usar 'Authentication=Active Directory Integrated' quando a propriedade Credential está definida. @@ -4345,16 +4345,16 @@ A instância {0} não dá suporte à criptografia de coluna. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Você especificou o URL de atestado de enclave e o protocolo de atestado na cadeia de conexão, mas o SQL Server em uso não oferece suporte a cálculos baseados em enclave - consulte https://go.microsoft.com/fwlink/?linkid=2157649 para mais detalhes. - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Você especificou o URL de atestado de enclave na cadeia de conexão, mas o SQL Server em uso não oferece suporte a cálculos baseados em enclave- consulte https://go.microsoft.com/fwlink/?linkid=2157649 para mais detalhes. - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Você especificou o protocolo de atestado na cadeia de conexão, mas o SQL Server em uso não oferece suporte a cálculos baseados em enclave - consulte https://go.microsoft.com/fwlink/?linkid=2157649 para mais detalhes. - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Você especificou o URL de atestado de enclave na cadeia de conexão, mas o SQL Server não retornou um tipo de enclave. Certifique-se de que o tipo de enclave está configurado corretamente na sua instância - consulte https://go.microsoft.com/fwlink/?linkid=2157649 para mais detalhes. {0} deve corresponder em todos os comandos ({1}, {2}, {3}, {4}) nas atualizações em lote. @@ -4459,7 +4459,7 @@ Tempo Limite de Execução Expirado. O período de tempo limite terminou antes da conclusão da operação ou o servidor não está respondendo. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + A validação de um token de atestado falhou. A assinatura do token não corresponde à assinatura computada usando uma chave pública recuperada do ponto de extremidade da chave pública de atestado em'{0}'. Verifique o aplicativo DNS para o ponto de extremidade - consulte https://go.microsoft.com/fwlink/?linkid=2157649 para mais detalhes. Se estiver correto, entre em contato com os Serviços de Suporte ao Cliente. Ocorreu um erro interno ao repetir o download do certificado raiz HGS após a falha da solicitação inicial. Entre em contato com os Serviços de Atendimento ao Cliente. @@ -4480,10 +4480,10 @@ A validação de um token de atestado falhou. O token tem um formato inválido. Entre em contato com os Serviços de Atendimento ao Cliente. Detalhes do erro: '{0}'. - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + O serviço de atestado retornou um certificado raiz HGS expirado para o URL de atestado '{0}'. Verifique o certificado raiz HGS configurado para sua instância HGS - consulte https://go.microsoft.com/fwlink/?linkid=2160553 para mais detalhes. - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + O certificado raiz HGS obtido para o URL de atestado '{0}' tem um formato inválido. Verifique se o URL de atestado está correto e se o servidor HGS está online e totalmente inicializado- consulte https://go.microsoft.com/fwlink/?linkid=2160553 para mais detalhes. Para obter suporte adicional, entre em contato com os Serviços de Suporte ao Cliente. Detalhes do erro: '{1}'. A validação de um token de atestado falhou. Não é possível recuperar uma chave pública do ponto de extremidade de chave pública de atestado ou a chave recuperada tem um formato inválido. Detalhes do erro: '{0}'. @@ -4501,31 +4501,31 @@ A validação do token de atestado falhou durante a validação de assinatura. Exceção: '{0}'. - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + A validação de um token de atestado falhou. A reivindicação '{0}' no token tem um valor inválido de '{1}'. Verifique a política de atestado - consulte https://go.microsoft.com/fwlink/?linkid=2157649 para mais detalhes. Se a política estiver correta, entre em contato com os Serviços de Suporte ao Cliente. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + A validação do token de atestado falhou. A reivindicação '{0}' está ausente no token. Verifique a política de atestado- consulte https://go.microsoft.com/fwlink/?linkid=2157649 para mais detalhes. Se a política estiver correta, entre em contato com os Serviços de Suporte ao Cliente. Falha ao verificar se o enclave está sendo executado no modo de produção. Entre em contato com os Serviços de Atendimento ao Cliente. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Não foi possível verificar a política de enclave devido a uma diferença entre os valores esperados e reais da política na propriedade '{0}'. Real: '{1}', Esperado: '{2}' - consulte https://go.microsoft.com/fwlink/?linkid=2160553 para mais detalhes. - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + A verificação da assinatura do relatório do enclave falhou. A assinatura do relatório não corresponde à assinatura calculada usando o certificado raiz HGS. Verifique o mapeamento DNS para o ponto de extremidade - consulte https://go.microsoft.com/fwlink/?linkid=2160553 para mais detalhes. Se estiver correto, entre em contato com os Serviços de Suporte ao Cliente. O relatório de enclave recebido do SQL Server não está no formato correto. Entre em contato com os Serviços de Atendimento ao Cliente. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + Falha ao criar uma cadeia de confiança entre o relatório de integridade do host de enclave e o certificado raiz HGS para o URL de atestado '{0}' com o status: '{1}'. Verifique se o URL de atestado corresponde ao URL configurado no SQL Server - consulte https://go.microsoft.com/fwlink/?linkid=2160553 para mais detalhes. Se o cliente e o SQL Server usarem o mesmo serviço de atestado, entre em contato com os Serviços de Suporte ao Cliente. Especifica um protocolo de atestado para o serviço de atestado de enclave correspondente. - Specifies an IP address preference when connecting to SQL instances. + Especifica uma preferência de endereço IP ao se conectar a instâncias SQL. Não há suporte para o tipo enclave '{0}' retornado do servidor. @@ -4579,13 +4579,13 @@ Não é possível definir a propriedade Credential quando 'Authentication=Active Directory Device Code Flow' está especificado na cadeia de conexão. - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + Não é possível definir a propriedade Credential quando 'Authentication={0}' é especificado na cadeia de conexão. Não é possível usar 'Authentication=Active Directory Device Code Flow' quando a propriedade Credential está configurada. - Cannot use 'Authentication={0}', if the Credential property has been set. + Não é possível usar 'Authentication={0}' quando a propriedade Credential está configurada. Tipo inesperado detectado na desserialização. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 4cda4f816b..fe96339d2d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -2503,7 +2503,7 @@ Невозможно использовать "Authentication=Active Directory Device Code Flow" с ключевыми словами строки подключения "User ID", "UID", "Password" или "PWD". - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + Невозможно использовать "Authentication={0}" с ключевыми словами Password или PWD в строке подключения. Не удается использовать "Authentication=Active Directory Integrated", если задано свойство "Credential". @@ -4345,16 +4345,16 @@ Используемый экземпляр {0} не поддерживает шифрование столбцов. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Вы указали в строке подключения URL-адрес и протокол аттестации анклава, но используемый SQL Server не поддерживает вычисления на основе анклава — см. https://go.microsoft.com/fwlink/?linkid=2157649 для получения дополнительных сведений. - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Вы указали URL-адрес аттестации анклава в строке подключения, но используемый SQL Server не поддерживает вычисления на основе анклава — см. https://go.microsoft.com/fwlink/?linkid=2157649 для получения дополнительных сведений. - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Вы указали в строке подключения протокол аттестации, но используемый SQL Server не поддерживает вычисления на основе анклава — см. https://go.microsoft.com/fwlink/?linkid=2157649 для получения дополнительных сведений. - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + В строке подключения указан URL-адрес аттестации анклава, но SQL Server не вернул тип анклава. Убедитесь, что тип анклава правильно настроен в вашем экземпляре — см. https://go.microsoft.com/fwlink/?linkid=2157649 для получения дополнительных сведений. Параметр {0} должен быть одинаковым для всех команд ({1}, {2}, {3}, {4}) при выполнении пакетных обновлений. @@ -4459,7 +4459,7 @@ Время ожидания выполнения истекло. Время ожидания истекло до завершения операции, или сервер не отвечает. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + Проверка токена аттестации не пройдена. Подпись токена не соответствует подписи, вычисленной на базе открытого ключа, полученного от конечной точки открытых ключей аттестации по адресу "{0}". Проверьте сопоставление DNS для конечной точки — см. https://go.microsoft.com/fwlink/?linkid=2157649 для получения дополнительных сведений. Если оно верно, обратитесь в службу поддержки пользователей. Произошла внутренняя ошибка при повторной попытке скачивания корневого сертификата HGS после сбоя начального запроса. Обратитесь в службу поддержки пользователей. @@ -4480,10 +4480,10 @@ Проверка токена аттестации не пройдена. Недопустимый формат токена. Обратитесь в службу поддержки пользователей. Сведения об ошибке: "{0}". - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Служба аттестации вернула просроченный корневой сертификат HGS для URL-адреса аттестации "{0}". Проверьте корневой сертификат HGS, настроенный для вашего экземпляра HGS — см. https://go.microsoft.com/fwlink/?linkid=2160553 для получения дополнительных сведений. - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + Полученный корневой сертификат HGS для URL-адреса аттестации "{0}" имеет недопустимый формат. Проверьте правильность URL-адреса аттестации и убедитесь, что сервер HGS подключен к сети и полностью инициализирован — см. https://go.microsoft.com/fwlink/?linkid=2160553 для получения дополнительных сведений. Для получения дополнительной поддержки обратитесь в службу поддержки пользователей. Сведения об ошибке: "{1}". Проверка токена аттестации не пройдена. Не удается получить открытый ключ из конечной точки открытого ключа аттестации либо полученный ключ имеет недопустимый формат. Сведения об ошибке: "{0}". @@ -4501,31 +4501,31 @@ Проверка токена аттестации не пройдена при проверке подписи. Исключение: "{0}". - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + Проверка токена аттестации не пройдена. Утверждение "{0}" в токене имеет недопустимое значение "{1}". Проверьте политику аттестации — см. https://go.microsoft.com/fwlink/?linkid=2157649 для получения дополнительных сведений. Если политика верна, обратитесь в службу поддержки пользователей. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + Проверка токена аттестации не пройдена. В токене отсутствует утверждение "{0}". Проверьте политику аттестации — см. https://go.microsoft.com/fwlink/?linkid=2157649 для получения дополнительных сведений. Если политика верна, обратитесь в службу поддержки пользователей. Не удалось проверить, работает ли анклав в рабочем режиме. Обратитесь в службу поддержки пользователей. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Не удалось проверить политику анклава из-за различий между ожидаемым и фактическим значениями политики в свойстве "{0}". Фактическое: "{1}", ожидаемое: "{2}" — см. https://go.microsoft.com/fwlink/?linkid=2160553 для дополнительных сведений. - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + Проверка подписи отчета анклава не пройдена. Подпись отчета не соответствует подписи, вычисленной с помощью корневого сертификата HGS. Проверьте сопоставление DNS для конечной точки — см. https://go.microsoft.com/fwlink/?linkid=2160553 для получения дополнительных сведений. Если оно верно, обратитесь в службу поддержки пользователей. Полученный с SQL Server отчет анклава имеет неправильный формат. Обратитесь в службу поддержки пользователей. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + Не удалось создать цепь доверия между отчетом о работоспособности узла анклава и корневым сертификатом HGS для URL-адреса аттестации "{0}" с состоянием: "{1}". Убедитесь, что URL-адрес аттестации совпадает с URL-адресом, настроенным на SQL Server — см. https://go.microsoft.com/fwlink/?linkid=2160553 для получения дополнительных сведений. Если клиент и SQL Server используют одну и ту же службу аттестации, обратитесь в службу поддержки пользователей. Указывает протокол аттестации для соответствующей службы аттестации анклава. - Specifies an IP address preference when connecting to SQL instances. + Указывает параметр IP-адреса при подключении к экземплярам SQL Server. Возвращаемый с сервера анклав типа "{0}" не поддерживается. @@ -4579,13 +4579,13 @@ Не удается задать свойство Credential, если в строке подключения указано "Authentication=Active Directory Device Code Flow". - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + Невозможно задать свойство Credential, если в строке подключения указано "Authentication={0}". Невозможно использовать ", если задано свойство Credential. - Cannot use 'Authentication={0}', if the Credential property has been set. + Невозможно использовать "Authentication={0}", если задано свойство Credential. Обнаружен непредвиденный тип при десериализации. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index dec4b0070f..42b7f9c362 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -2503,7 +2503,7 @@ 无法结合使用 "Authentication=Active Directory Device Code Flow" 与 "User ID"、"UID"、"Password" 或 "PWD" 连接字符串关键字。 - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + 无法将 "Authentication={0}" 与 "Password" 或 "PWD" 连接字符串关键字结合使用。 如果设置了 Credential 属性,则无法使用“Authentication=Active Directory Integrated”。 @@ -4579,13 +4579,13 @@ 如果在连接字符串中指定了 "Authentication=Active Directory Device Code Flow",则无法设置 Credential 属性。 - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + 如果在连接字符串中指定了 "Authentication={0}",则无法设置 Credential 属性。 如果设置了 Credential 属性,则无法使用 "Authentication=Active Directory Device Code Flow"。 - Cannot use 'Authentication={0}', if the Credential property has been set. + 如果设置了 Credential 属性,则无法使用 "Authentication={0}"。 反序列化时检测到意外的类型。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 35aa363edb..53fecc1fc7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -2503,7 +2503,7 @@ 無法搭配 'User ID'、'UID'、'Password' 或 'PWD' 連接字串關鍵字使用 'Authentication=Active Directory Device Code Flow'。 - Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. + 使用 'Authentication={0}' 時,無法搭配 'Password' 或 'PWD' 連接字串關鍵字一起使用。 如果已設定 Credential 屬性,則無法使用 'Authentication=Active Directory Integrated'。 @@ -4345,16 +4345,16 @@ 使用中的 {0} 執行個體不支援資料行加密。 - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 您已在連接字串中指定記憶體保護區證明 URL 和證明通訊協定,但使用中的 SQL Server 不支援以記憶體保護區為基礎的計算 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2157649 尋求 詳細 資訊。 - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 您已在連接字串中指定記憶體保護區證明 URL,但使用中的 SQL Server 不支援以記憶體保護區為基礎的計算 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2157649 尋求 詳細 資訊。 - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 您已在連接字串中指定證明通訊協定,但使用中的 SQL Server 不支援以記憶體保護區為基礎的計算 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2157649 尋求 詳細 資訊。 - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 您在連接字串中指定了記憶體保護區證明 URL,但 SQL Server 執行個體未傳回記憶體保護區類型。請確定您執行個體中的記憶體保護區類型設定正確 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2157649 尋求 詳細 資訊。 執行批次更新時,所有命令 ({1}、{2}、{3}、{4}) 中的 {0} 必須一致。 @@ -4459,7 +4459,7 @@ 執行逾時到期。在作業完成之前超過逾時等待的時間,或是伺服器未回應。 - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + 驗證證明權杖失敗。該權杖簽章與使用從位於 '{0}' 的證明公開金鑰端點擷取而來之公開金鑰計算所得的簽章不符。請驗證該端點的 DNS 對應 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2157649 尋求 詳細 資訊。若其正確,請連絡客戶支援服務。 初始要求失敗後,重試下載 HGS 根憑證的下載時發生內部錯誤。請連絡客戶支援服務。 @@ -4480,10 +4480,10 @@ 驗證證明權杖失敗。權杖的格式無效。請連絡客戶支援服務。錯誤詳細資料: '{0}'。 - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + 證明服務為證明 URL '{0}' 傳回了過期的 HGS 根憑證。請檢查為您 HGS 執行個體設定的 HGS 根憑證 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2160553 尋求 詳細 資訊。 - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + 為證明 URL '{0}' 取得的 HGS 根憑證格式無效。請驗證證明 URL 正確,而且 HGS 伺服器在線上並已完全初始化 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2160553 尋求 詳細 資訊。如需額外支援,請連絡客戶支援服務。錯誤詳細資料: '{1}'。 驗證證明權杖失敗。無法從證明公開金鑰端點取得公開金鑰,或擷取到了無效格式的金鑰。錯誤詳細資料: '{0}'。 @@ -4501,31 +4501,31 @@ 驗證簽章時,驗證證明權杖失敗。例外狀況: '{0}'。 - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + 驗證證明權杖失敗。權杖中的宣告 '{0}' 有 '{1}'的無效值。請驗證證明原則 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2157649 尋求 詳細 資訊。若該原則正確,請連絡客戶支援服務。 - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + 驗證證明權杖失敗。權杖中缺少宣告 '{0}'。請驗證證明原則 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2157649 尋求 詳細 資訊。若該原則正確,請連絡客戶支援服務。 無法檢查記憶體保護區是否在生產模式下執行。請連絡客戶支援服務。 - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + 因為屬性 '{0}' 上原則的預期值與實際值不同,所以無法驗證記憶體保護區原則。實際: '{1}',預期: '{2}' - 請參閱 https://go.microsoft.com/fwlink/?linkid=2160553 尋求 詳細 資訊。 - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + 驗證記憶體保護區報表的簽章失敗。報表的簽章與使用 HGS 根憑證計算所得的簽章不符。請驗證端點的 DNS 對應 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2160553 尋求 詳細 資訊。若其正確,請連絡客戶支援服務。 從 SQL Server 收到的記憶體保護區報表的格式不正確。請連絡客戶支援服務。 - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + 無法在記憶體保護區主機的健康狀態報表與證明 URL '{0}' 的 HGS 根憑證之間建立信任鏈結,狀態: '{1}'。請驗證證明 URL 與 SQL Server 上設定的 URL 相符 - 請參閱 https://go.microsoft.com/fwlink/?linkid=2160553 尋求 詳細 資訊。如果用戶端和 SQL Server 都使用相同的證明服務,請連絡客戶支援服務。 為證明通訊協定的對應記憶體保護區證明服務指定證明通訊協定。 - Specifies an IP address preference when connecting to SQL instances. + 指定連線到 SQL 執行個體時的 IP 位址喜好設定。 不支援從伺服器傳回的記憶體保護區類型 '{0}'。 @@ -4579,13 +4579,13 @@ 如果連接字串中已指定 'Authentication=Active Directory Device Code Flow',則無法設定 Credential 屬性。 - Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. + 如果連接字串中已指定 'Authentication={0}',則無法設定 Credential 屬性。 如果已設定 Credential 屬性,則無法使用 'Authentication=Active Directory Device Code Flow'。 - Cannot use 'Authentication={0}', if the Credential property has been set. + 如果已設定 Credential 屬性,就無法使用 'Authentication={0}'。 還原序列化時偵測到未預期的類型。 From 926ed8b93594253236aa9fe9c0666604c27ed753 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 19 May 2021 19:03:34 -0700 Subject: [PATCH 42/87] Docs | Switch to new docs for Microsoft.Data.SqlClient (#1075) --- .../SqlNotificationRequest.xml | 12 +- .../SqlMetaData.xml | 20 +- .../Microsoft.Data.SqlClient/SqlBulkCopy.xml | 60 ++--- .../SqlBulkCopyColumnMapping.xml | 20 +- .../SqlBulkCopyColumnMappingCollection.xml | 18 +- .../SqlBulkCopyColumnOrderHint.xml | 8 +- .../SqlBulkCopyColumnOrderHintCollection.xml | 12 +- .../SqlBulkCopyOptions.xml | 2 +- .../Microsoft.Data.SqlClient/SqlCommand.xml | 230 +++++++++--------- .../SqlCommandBuilder.xml | 26 +- .../SqlConnection.xml | 42 ++-- .../SqlConnectionStringBuilder.xml | 14 +- .../SqlCredential.xml | 8 +- .../SqlDataAdapter.xml | 8 +- .../SqlDataReader.xml | 16 +- .../SqlDependency.xml | 2 +- .../Microsoft.Data.SqlClient/SqlException.xml | 2 +- .../Microsoft.Data.SqlClient/SqlParameter.xml | 26 +- .../SqlParameterCollection.xml | 2 +- .../Microsoft.Data.SqlTypes/SqlFileStream.xml | 2 +- 20 files changed, 265 insertions(+), 265 deletions(-) diff --git a/doc/snippets/Microsoft.Data.Sql/SqlNotificationRequest.xml b/doc/snippets/Microsoft.Data.Sql/SqlNotificationRequest.xml index 4c3f6bd5f9..8a60e5c16c 100644 --- a/doc/snippets/Microsoft.Data.Sql/SqlNotificationRequest.xml +++ b/doc/snippets/Microsoft.Data.Sql/SqlNotificationRequest.xml @@ -11,7 +11,7 @@ This class provides low-level access to the query notification services exposed ]]> - Using Query Notifications + Using Query Notifications Creates a new instance of the class with default values. @@ -23,7 +23,7 @@ If the parameterless constructor is used to create a - Using Query Notifications + Using Query Notifications A string that contains an application-specific identifier for this notification. It is not used by the notifications infrastructure, but it allows you to associate notifications with the application state. The value indicated in this parameter is included in the Service Broker queue message. @@ -40,7 +40,7 @@ This constructor allows you to initialize a new The value of the parameter is NULL. The or parameter is longer than or the value in the parameter is less than zero. - Using Query Notifications + Using Query Notifications Gets or sets the SQL Server Service Broker service name where notification messages are posted. @@ -64,7 +64,7 @@ The SQL Server Service Broker service must be previously configured on the serve The value is NULL. The value is longer than . - Using Query Notifications + Using Query Notifications Gets or sets a value that specifies how long SQL Server waits for a change to occur before the operation times out. @@ -78,7 +78,7 @@ After the time-out period expires, the notification is sent even if no change ta ]]> The value is less than zero. - Using Query Notifications + Using Query Notifications Gets or sets an application-specific identifier for this notification. @@ -92,7 +92,7 @@ This value is not used by the notifications infrastructure. Instead, it is a mec ]]> The value is longer than . - Using Query Notifications + Using Query Notifications diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml index bf88376dd5..016b6e6ee3 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml +++ b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml @@ -218,7 +218,7 @@ The following are the default values assigned to `dbType`, depending on the `Sql @@ -236,7 +236,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -255,7 +255,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -321,7 +321,7 @@ The following are the default values assigned to `dbType`, depending on the `Sql @@ -341,7 +341,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -361,7 +361,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -384,7 +384,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -692,7 +692,7 @@ The default is `FALSE`. This property can only be set in one of the constructors. -For more information, see [Table-Valued Parameters](/dotnet/framework/data/adonet/sql/table-valued-parameters). +For more information, see [Table-Valued Parameters](/sql/connect/ado-net/sql/table-valued-parameters). ]]> @@ -777,7 +777,7 @@ Returns 0 if the scale of the underlying column type is not defined. ## Remarks This property can only be set in one of the constructors. -For more information, see [Table-Valued Parameters](/dotnet/framework/data/adonet/sql/table-valued-parameters). +For more information, see [Table-Valued Parameters](/sql/connect/ado-net/sql/table-valued-parameters). ]]> @@ -793,7 +793,7 @@ The default is 1. This property can only be set in one of the constructors. -For more information, see [Table-Valued Parameters](/dotnet/framework/data/adonet/sql/table-valued-parameters). +For more information, see [Table-Valued Parameters](/sql/connect/ado-net/sql/table-valued-parameters). ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml index 970d74c289..4388f98172 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml @@ -15,7 +15,7 @@ The following console application demonstrates how to load data using the is used to copy data from the **Production.Product** table in the SQL Server **AdventureWorks** database to a similar table in the same database. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -55,7 +55,7 @@ Note that the source data does not have to be located on SQL Server; you can use . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -102,14 +102,14 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. ## Remarks If options include `UseInternalTransaction` and the `externalTransaction` argument is not null, an **InvalidArgumentException** is thrown. -For examples demonstrating how to use `SqlBulkCopy` in a transaction, see [Transaction and Bulk Copy Operations](/dotnet/framework/data/adonet/sql/transaction-and-bulk-copy-operations). +For examples demonstrating how to use `SqlBulkCopy` in a transaction, see [Transaction and Bulk Copy Operations](/sql/connect/ado-net/sql/transaction-bulk-copy-operations). ]]> Performing Bulk Copy Operations - - ADO.NET Overview + + Overview of the SqlClient driver @@ -152,7 +152,7 @@ In this example, the source data is first read from a SQL Server table to a or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -216,7 +216,7 @@ Then run the sample again without emptying the table. An exception is thrown and added because of primary key constraint violations. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -252,10 +252,10 @@ operations on the same instance use ## Examples The following console application demonstrates how to bulk load data in batches of 50 rows. For an example illustrating how -works with a transaction, see [Transaction and Bulk Copy Operations](/dotnet/framework/data/adonet/sql/transaction-and-bulk-copy-operations). +works with a transaction, see [Transaction and Bulk Copy Operations](/sql/connect/ado-net/sql/transaction-bulk-copy-operations). > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -284,7 +284,7 @@ In this example, the source data is first read from a SQL Server table to a or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -307,7 +307,7 @@ Note that open instances are closed The following example uses the same instance to add sales orders and their associated details to two destination tables. Because the **AdventureWorks** sales order tables are large, the sample reads only orders placed by a certain account number and bulk copies those orders and details to the destination tables. The method is used only after both bulk copy operations are complete. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.OrdersDetails#1](~/../sqlclient/doc/samples/SqlBulkCopy_OrdersDetails.cs#1)] ]]> @@ -406,7 +406,7 @@ In this example, the connection is first used to read data from a SQL Server tab be located on SQL Server; you can use any data source that can be read to an or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.Single#1](~/../sqlclient/doc/samples/SqlBulkCopy_Single.cs#1)] @@ -441,7 +441,7 @@ In this example, the connection is first used to read data from a SQL Server tab Note that the source data does not have to be located on SQL Server; you can use any data source that can be read to an or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -466,7 +466,7 @@ Note that the settings of ) or SqlConnection.Close () from this event. Doing this will cause an being thrown, and the object state will not change. If the user wants to cancel the operation from the event, the property of the can be used. -(See [Transaction and Bulk Copy Operations](/dotnet/framework/data/adonet/sql/transaction-and-bulk-copy-operations) for examples that use the +(See [Transaction and Bulk Copy Operations](/sql/connect/ado-net/sql/transaction-bulk-copy-operations) for examples that use the property.) No action, such as transaction activity, is supported in the connection during the execution of the bulk copy operation, and it is recommended that you not use the same connection used @@ -481,7 +481,7 @@ In this example, the connection is first used to read data from a SQL Server tab SQL Server; you can use any data source that can be read to an or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -588,7 +588,7 @@ The collection maps f The following console application demonstrates how to bulk load data from a . The destination table is a table in the **AdventureWorks** database. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -632,7 +632,7 @@ The following Console application demonstrates how to bulk load data from a is created at run time and is the source of the `SqlBulkCopy` operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.DataTable#1](~/../sqlclient/doc/samples/SqlBulkCopy_DataTable.cs#1)] @@ -646,8 +646,8 @@ This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. Performing Bulk Copy Operations - - ADO.NET Overview + + Overview of the SqlClient driver @@ -692,7 +692,7 @@ In this example, a is created at run time and three The method is called with a `DataRowState.Unchanged` `rowState` argument, so only the two unchanged rows are bulk copied to the destination. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.DataRowState#1](~/../sqlclient/doc/samples/SqlBulkCopy_DataRowState.cs#1)] ]]> @@ -720,7 +720,7 @@ The following console application demonstrates how to bulk load data from a is created at run time. A single row is selected from the to copy to the destination table. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.RowArray#1](~/../sqlclient/doc/samples/SqlBulkCopy_RowArray.cs#1)] @@ -755,7 +755,7 @@ In this example, a is created at run time. A single @@ -820,7 +820,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -878,7 +878,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -980,7 +980,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1059,7 +1059,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1129,7 +1129,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1196,7 +1196,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1256,7 +1256,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1328,7 +1328,7 @@ For more information about asynchronous programming in the .NET Framework Data P diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMapping.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMapping.xml index 51925e7dfa..f8d258ff32 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMapping.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMapping.xml @@ -26,7 +26,7 @@ destination matches the number of columns in the source, and each destination co objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -54,7 +54,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -81,7 +81,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy based on the ordinal positions of the columns. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -108,7 +108,7 @@ the destination matches the number of columns in the source, the column names an column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -135,7 +135,7 @@ the destination matches the number of columns in the source, the column names an a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -162,7 +162,7 @@ destination matches the number of columns in the source, the column names and or column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -192,7 +192,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -222,7 +222,7 @@ destination matches the number of columns in the source, the column names and or column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -252,7 +252,7 @@ destination matches the number of columns in the source, the column names and or column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -282,7 +282,7 @@ matches the number of columns in the source, the column names and ordinal positi bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMappingCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMappingCollection.xml index 145e8407a0..5c8ddd2ef3 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMappingCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMappingCollection.xml @@ -23,7 +23,7 @@ Although the number of columns in the destination matches the number of columns object to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -45,7 +45,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -73,7 +73,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy using the ordinal position of the source and destination columns. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -100,7 +100,7 @@ destination matches the number of columns in the source, the column names and or column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -127,7 +127,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -154,7 +154,7 @@ Although the number of columns in the destination matches the number of columns object by specifying the column names. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -182,7 +182,7 @@ Although not strictly necessary in this example (because the ordinal positions o The method must be used after the first bulk copy is performed and before the next bulk copy's column mappings are defined. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -249,7 +249,7 @@ Both bulk copies include a mapping for the **SalesOrderID**, so rather than clea mapping and then adds the appropriate mappings for the second bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -281,7 +281,7 @@ Both bulk copies include a mapping for the **SalesOrderID**, so rather than clea **SalesOrderID** mapping and then adds the appropriate mappings for the second bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index ff7a7130f4..d3e846546c 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -31,7 +31,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -58,7 +58,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -88,7 +88,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -118,7 +118,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index 662ad0e652..1f6912efa5 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -29,7 +29,7 @@ The following example bulk copies data from a source table in the **AdventureWor object to specify order hints for the bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -51,7 +51,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the **ProductNumber** destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -78,7 +78,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is added to the by providing the destination column name and its sort order. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -107,7 +107,7 @@ The example defines a column order hint for each bulk copy operation. The method must be used after the first bulk copy is performed and before the next bulk copy's order hint is defined. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -176,7 +176,7 @@ The following example performs two bulk copy operations. The first operation cop The example defines a column order hint for the **OrderDate** column in the first bulk copy operation. The hint is removed before the second bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -208,7 +208,7 @@ The following example performs two bulk copy operations. The first operation cop The example defines a column order hint for the **OrderDate** column in the first bulk copy operation. The hint is removed before the second bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyOptions.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyOptions.xml index f1433ee4ff..152fcbde42 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyOptions.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyOptions.xml @@ -18,7 +18,7 @@ Next, run the sample again without emptying the table. An exception is thrown, a primary key violations. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index f9e30cc2cb..399797cb56 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -254,7 +254,7 @@ The following console application creates updates data within the **AdventureWor was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -281,12 +281,12 @@ The following console application creates updates data within the **AdventureWor -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -295,7 +295,7 @@ The following console application creates updates data within the **AdventureWor or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -304,7 +304,7 @@ The following console application creates updates data within the **AdventureWor or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -367,7 +367,7 @@ To set up this example, create a new Windows application. Put a was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -394,12 +394,12 @@ To set up this example, create a new Windows application. Put a The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -445,7 +445,7 @@ The following console application starts the process of retrieving a data reader was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -472,12 +472,12 @@ The following console application starts the process of retrieving a data reader -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -486,7 +486,7 @@ The following console application starts the process of retrieving a data reader or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -495,7 +495,7 @@ The following console application starts the process of retrieving a data reader or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -552,7 +552,7 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -579,12 +579,12 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -593,7 +593,7 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -602,7 +602,7 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -670,7 +670,7 @@ To set up this example, create a new Windows application. Put a was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -697,12 +697,12 @@ To set up this example, create a new Windows application. Put a The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -711,7 +711,7 @@ To set up this example, create a new Windows application. Put a or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -720,7 +720,7 @@ To set up this example, create a new Windows application. Put a or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -799,7 +799,7 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -826,12 +826,12 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -840,7 +840,7 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -849,7 +849,7 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -907,7 +907,7 @@ The following console application starts the process of retrieving XML data asyn was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -934,12 +934,12 @@ The following console application starts the process of retrieving XML data asyn -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -948,7 +948,7 @@ The following console application starts the process of retrieving XML data asyn or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -957,7 +957,7 @@ The following console application starts the process of retrieving XML data asyn or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1035,7 +1035,7 @@ To set up this example, create a new Windows application. Put a was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1062,12 +1062,12 @@ To set up this example, create a new Windows application. Put a The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1076,7 +1076,7 @@ To set up this example, create a new Windows application. Put a or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1085,7 +1085,7 @@ To set up this example, create a new Windows application. Put a or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1154,7 +1154,7 @@ The Microsoft .NET Framework Data Provider for SQL Server does not support the q SELECT * FROM dbo.Customers WHERE CustomerID = @CustomerID ``` -For more information, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). +For more information, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). ## Examples The following example creates a and sets some of its properties. @@ -1212,7 +1212,7 @@ The Microsoft .NET Framework Data Provider for SQL Server does not support the q SELECT * FROM Customers WHERE CustomerID = @CustomerID -For more information, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). +For more information, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). ## Examples The following example creates a and sets some of its properties. @@ -1561,7 +1561,7 @@ The following example creates a and t was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1588,7 +1588,7 @@ The following example creates a and t -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1597,12 +1597,12 @@ The following example creates a and t or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1611,7 +1611,7 @@ The following example creates a and t or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1630,7 +1630,7 @@ The following example creates a and t @@ -1640,7 +1640,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1671,14 +1671,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1687,7 +1687,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1696,7 +1696,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1741,7 +1741,7 @@ The following example creates a , and was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1768,7 +1768,7 @@ The following example creates a , and -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The current state of the connection is closed. @@ -1781,7 +1781,7 @@ The following example creates a , and The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1790,7 +1790,7 @@ The following example creates a , and or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1799,7 +1799,7 @@ The following example creates a , and or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1850,7 +1850,7 @@ The following example creates a , and was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1873,7 +1873,7 @@ The following example creates a , and . - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1882,12 +1882,12 @@ The following example creates a , and or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1896,7 +1896,7 @@ The following example creates a , and or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1918,7 +1918,7 @@ The following example creates a , and @@ -1928,7 +1928,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1964,14 +1964,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1980,7 +1980,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1989,7 +1989,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2018,7 +2018,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -2028,7 +2028,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2062,14 +2062,14 @@ For more information about asynchronous programming in the .NET Framework Data P -or- - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2078,7 +2078,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2087,7 +2087,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2114,7 +2114,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -2125,7 +2125,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2161,14 +2161,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2177,7 +2177,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2186,7 +2186,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2218,7 +2218,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -2228,7 +2228,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2264,14 +2264,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2280,7 +2280,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2289,7 +2289,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2327,7 +2327,7 @@ The following example creates a and t was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2354,12 +2354,12 @@ The following example creates a and t -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2368,7 +2368,7 @@ The following example creates a and t or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2377,7 +2377,7 @@ The following example creates a and t or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2398,7 +2398,7 @@ The following example creates a and t @@ -2408,7 +2408,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2439,14 +2439,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2455,7 +2455,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2464,7 +2464,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2514,7 +2514,7 @@ The following example creates a and t was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2541,12 +2541,12 @@ The following example creates a and t -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2555,7 +2555,7 @@ The following example creates a and t or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2564,7 +2564,7 @@ The following example creates a and t or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2589,7 +2589,7 @@ The following example creates a and t ## Remarks The **XmlReader** returned by this method does not support asynchronous operations. -For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming). +For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming). ]]> @@ -2599,7 +2599,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2630,14 +2630,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2646,7 +2646,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2655,7 +2655,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2683,7 +2683,7 @@ For more information about asynchronous programming in the .NET Framework Data P ## Remarks The **XmlReader** returned by this method does not support asynchronous operations. -For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming). +For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming). ]]> @@ -2693,7 +2693,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2724,14 +2724,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2740,7 +2740,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2749,7 +2749,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2892,7 +2892,7 @@ SELECT * FROM Customers WHERE CustomerID = @CustomerID > [!NOTE] > If the parameters in the collection do not match the requirements of the query to be executed, an error may result. -For more information, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). +For more information, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). ## Examples The following example demonstrates how to create a and add parameters to the . @@ -2995,7 +2995,7 @@ You cannot set the pro ## Remarks The default value is **Both** unless the command is automatically generated (as in the case of the ), in which case the default is **None**. -For more information about using the **UpdatedRowSource** property, see [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters). +For more information about using the **UpdatedRowSource** property, see [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters). ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml index e242e17c2c..87ddd2c8b8 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml @@ -142,7 +142,7 @@ When you create a new instance of with arbitrary Transact-SQL statements, such as a parameterized SELECT statement. -For more information, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). +For more information, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). ]]> @@ -165,7 +165,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -183,7 +183,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -210,7 +210,7 @@ The default behavior, when generating parameter names, is to use `@p1`, `@p2`, a - A returned from the **GetSchema** method call and found in the collection is specified. -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -227,7 +227,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The Transact-SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -245,7 +245,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The Transact-SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -272,7 +272,7 @@ The default behavior, when generating parameter names, is to use `@p1`, `@p2`, a - A returned from the **GetSchema** method call and found in the collection is specified. -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -313,7 +313,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The Transact-SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -331,7 +331,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The Transact-SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -358,7 +358,7 @@ The default behavior, when generating parameter names, is to use `@p1`, `@p2`, a - A returned from the **GetSchema** method call and found in the collection is specified. -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -431,9 +431,9 @@ Generally, database servers indicate the schema for a identifier by separating t Given a quoted identifier, returns the correct unquoted form of that identifier. This includes correctly unescaping any embedded quotes in the identifier. The unquoted identifier, with embedded quotes properly unescaped. To be added. - Connecting and Retrieving Data in ADO.NET - Using the .NET Framework Data Provider for SQL Server - ADO.NET Overview + Connecting and Retrieving Data in ADO.NET + Using the .NET Framework Data Provider for SQL Server + Overview of the SqlClient driver diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 7eee99980d..d7da726364 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -16,7 +16,7 @@ If the goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling `Close` or `Dispose`. `Close` and `Dispose` are functionally equivalent. If the connection pooling value `Pooling` is set to `true` or `yes`, the underlying connection is returned back to the connection pool. On the other hand, if `Pooling` is set to `false` or `no`, the underlying connection to the server is actually closed. > [!NOTE] -> Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). +> Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). To ensure that connections are always closed, open the connection inside of a `using` block, as shown in the following code fragment. Doing so ensures that the connection is automatically closed when the code exits the block. @@ -37,13 +37,13 @@ using (SqlConnection connection = new SqlConnection(connectionString)) ``` > [!NOTE] -> To deploy high-performance applications, you must use connection pooling. When you use the .NET Framework Data Provider for SQL Server, you do not have to enable connection pooling because the provider manages this automatically, although you can modify some settings. For more information, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). +> To deploy high-performance applications, you must use connection pooling. When you use the .NET Framework Data Provider for SQL Server, you do not have to enable connection pooling because the provider manages this automatically, although you can modify some settings. For more information, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). If a is generated by the method executing a , the remains open when the severity level is 19 or less. When the severity level is 20 or greater, the server ordinarily closes the . However, the user can reopen the connection and continue. An application that creates an instance of the object can require all direct and indirect callers to have sufficient permission to the code by setting declarative or imperative security demands. makes security demands using the object. Users can verify that their code has sufficient permissions by using the object. Users and administrators can also use the [Caspol.exe (Code Access Security Policy Tool)](/dotnet/framework/tools/caspol-exe-code-access-security-policy-tool) to modify security policy at the machine, user, and enterprise levels. For more information, see [Security in .NET](/dotnet/standard/security/). For an example demonstrating how to use security demands, see [Code Access Security and ADO.NET](/dotnet/framework/data/adonet/code-access-security). - For more information about handling warning and informational messages from the server, see [Connection Events](/dotnet/framework/data/adonet/connection-events). For more information about SQL Server engine errors and error messages, see [Database Engine Events and Errors](/sql/relational-databases/errors-events/database-engine-events-and-errors). + For more information about handling warning and informational messages from the server, see [Connection Events](/sql/connect/ado-net/connection-events). For more information about SQL Server engine errors and error messages, see [Database Engine Events and Errors](/sql/relational-databases/errors-events/database-engine-events-and-errors). > [!CAUTION] > You can force TCP instead of shared memory. You can do that by prefixing tcp: to the server name in the connection string or you can use localhost. @@ -429,7 +429,7 @@ End Module If the goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling `Close` or `Dispose`. `Close` and `Dispose` are functionally equivalent. If the connection pooling value `Pooling` is set to `true` or `yes`, the underlying connection is returned back to the connection pool. On the other hand, if `Pooling` is set to `false` or `no`, the underlying connection to the server is closed. > [!NOTE] -> Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). +> Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). > [!CAUTION] > Do not call `Close` or `Dispose` on a Connection, a DataReader, or any other managed object in the `Finalize` method of your class. In a finalizer, you should only release unmanaged resources that your class owns directly. If your class does not own any unmanaged resources, do not include a `Finalize` method in your class definition. For more information, see [Garbage Collection](/dotnet/standard/garbage-collection/). @@ -501,7 +501,7 @@ End Module "Persist Security Info=False;Integrated Security=true;Initial Catalog=Northwind;server=(local)" ``` - Use the new to construct valid connection strings at run time. For more information, see [Connection String Builders](/dotnet/framework/data/adonet/connection-string-builders). + Use the new to construct valid connection strings at run time. For more information, see [Connection String Builders](/sql/connect/ado-net/connection-string-builders). The property can be set only when the connection is closed. Many of the connection string values have corresponding read-only properties. When the connection string is set, these properties are updated, except when an error is detected. In this case, none of the properties are updated. properties return only those settings that are contained in the . @@ -525,10 +525,10 @@ End Module |Address|N/A|Synonym of **Data Source**.| |App|N/A|Synonym of **Application Name**.| |Application Name|N/A|The name of the application, or '.NET SQLClient Data Provider' if no application name is provided.

An application name can be 128 characters or less.| -|Application Intent

-or-

ApplicationIntent|ReadWrite|Declares the application workload type when connecting to a server. Possible values are `ReadOnly` and `ReadWrite`. For example:

`ApplicationIntent=ReadOnly`

For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery).| -|Asynchronous Processing

-or-

Async|'false'|This property is obsolete and should not used.

When `true`, enables asynchronous operation support. Recognized values are `true`, `false`, `yes`, and `no`.

This property is ignored beginning in .NET Framework 4.5. For more information about SqlClient support for asynchronous programming, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming).| -|Attestation Protocol|N/A|Gets or sets the value of Attestation Protocol.

Valid values are:
`AAS`
`HGS`| +|Application Intent

-or-

ApplicationIntent|ReadWrite|Declares the application workload type when connecting to a server. Possible values are `ReadOnly` and `ReadWrite`. For example:

`ApplicationIntent=ReadOnly`

For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery).| +|Asynchronous Processing

-or-

Async|'false'|This property is obsolete and should not used.

When `true`, enables asynchronous operation support. Recognized values are `true`, `false`, `yes`, and `no`.

This property is ignored beginning in .NET Framework 4.5. For more information about SqlClient support for asynchronous programming, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming).| |AttachDBFilename

-or-

Extended Properties

-or-

Initial File Name|N/A|The name of the primary database file, including the full path name of an attachable database. AttachDBFilename is only supported for primary data files with an .mdf extension.

If the value of the AttachDBFileName key is specified in the connection string, the database is attached and becomes the default database for the connection.

If this key is not specified and if the database was previously attached, the database will not be reattached. The previously attached database will be used as the default database for the connection.

If this key is specified together with the AttachDBFileName key, the value of this key will be used as the alias. However, if the name is already used in another attached database, the connection will fail.

The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string. **Note:** Remote server, HTTP, and UNC path names are not supported.

The database name must be specified with the keyword 'database' (or one of its aliases) as in the following:

"AttachDbFileName=|DataDirectory|\data\YourDB.mdf;integrated security=true;database=YourDatabase"

An error will be generated if a log file exists in the same directory as the data file and the 'database' keyword is used when attaching the primary data file. In this case, remove the log file. Once the database is attached, a new log file will be automatically generated based on the physical path.| +|Attestation Protocol|N/A|Gets or sets the value of Attestation Protocol.

Valid values are:
`AAS`
`HGS`| |Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).

Valid values are:

`Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, `Sql Password`. Currently `Active Directory Integrated` and `Active Directory Interactive` modes of authentication are supported only for .NET Framework. | |Column Encryption Setting|N/A|Enables or disables [Always Encrypted](/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-2017) functionality for the connection.| |Command Timeout|30|The default wait time (in seconds) before terminating the attempt to execute a command and generating an error.

Valid values are greater than or equal to 0 and less than or equal to 2147483647.| @@ -537,17 +537,17 @@ End Module |Connect Retry Count

-or-

ConnectRetryCount|1|Controls the number of reconnection attempts after the client identifies an idle connection failure. Valid values are 0 to 255. The default is 1. 0 means do not attempt to reconnect (disable connection resiliency).

For additional information about idle connection resiliency, see [Technical Article - Idle Connection Resiliency](https://go.microsoft.com/fwlink/?LinkId=393996).| |Connect Retry Interval

-or-

ConnectRetryInterval|10|Specifies the time between each connection retry attempt (ConnectRetryCount). Valid values are 1 to 60 seconds (default=10), applied after the first reconnection attempt. When a broken connection is detected, the client immediately attempts to reconnect; this is the first reconnection attempt and only occurs if ConnectRetryCount is greater than 0. If the first reconnection attempt fails and ConnectRetryCount is greater than 1, the client waits ConnectRetryInterval to try the second and subsequent reconnection attempts.

For additional information about idle connection resiliency, see [Technical Article - Idle Connection Resiliency](https://go.microsoft.com/fwlink/?LinkId=393996).| |Current Language

-or-

Language|N/A|Sets the language used for database server warning or error messages.

The language name can be 128 characters or less.| -|Data Source

-or-

Server

-or-

Address

-or-

Addr

-or-

Network Address|N/A|The name or network address of the instance of SQL Server to which to connect. The port number can be specified after the server name:

`server=tcp:servername, portnumber`

When specifying a local instance, always use (local). To force a protocol, add one of the following prefixes:

`np:(local), tcp:(local), lpc:(local)`

Beginning in .NET Framework 4.5, you can also connect to a LocalDB database as follows:

`server=(localdb)\\myInstance`

For more information about LocalDB, see [SqlClient Support for LocalDB](/dotnet/framework/data/adonet/sql/sqlclient-support-for-localdb).

**Data Source** must use the TCP format or the Named Pipes format.

TCP format is as follows:

- tcp:\\\
- tcp:\,\

The TCP format must start with the prefix "tcp:" and is followed by the database instance, as specified by a host name and an instance name. This format is not applicable when connecting to Azure SQL Database. TCP is automatically selected for connections to Azure SQL Database when no protocol is specified.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The instance name is used to resolve to a particular TCP/IP port number on which a database instance is hosted. Alternatively, specifying a TCP/IP port number directly is also allowed. If both instance name and port number are not present, the default database instance is used.

The Named Pipes format is as follows:

- np:\\\\\pipe\\

The Named Pipes format MUST start with the prefix "np:" and is followed by a named pipe name.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The pipe name is used to identify the database instance to which the .NET Framework application will be connected.

If the value of the **Network** key is specified, the prefixes "tcp:" and "np:" should not be specified. **Note:** You can force the use of TCP instead of shared memory, either by prefixing **tcp:** to the server name in the connection string, or by using **localhost**.| +|Data Source

-or-

Server

-or-

Address

-or-

Addr

-or-

Network Address|N/A|The name or network address of the instance of SQL Server to which to connect. The port number can be specified after the server name:

`server=tcp:servername, portnumber`

When specifying a local instance, always use (local). To force a protocol, add one of the following prefixes:

`np:(local), tcp:(local), lpc:(local)`

Beginning in .NET Framework 4.5, you can also connect to a LocalDB database as follows:

`server=(localdb)\\myInstance`

For more information about LocalDB, see [SqlClient Support for LocalDB](/sql/connect/ado-net/sql/sqlclient-support-localdb).

**Data Source** must use the TCP format or the Named Pipes format.

TCP format is as follows:

- tcp:\\\
- tcp:\,\

The TCP format must start with the prefix "tcp:" and is followed by the database instance, as specified by a host name and an instance name. This format is not applicable when connecting to Azure SQL Database. TCP is automatically selected for connections to Azure SQL Database when no protocol is specified.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The instance name is used to resolve to a particular TCP/IP port number on which a database instance is hosted. Alternatively, specifying a TCP/IP port number directly is also allowed. If both instance name and port number are not present, the default database instance is used.

The Named Pipes format is as follows:

- np:\\\\\pipe\\

The Named Pipes format MUST start with the prefix "np:" and is followed by a named pipe name.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The pipe name is used to identify the database instance to which the .NET Framework application will be connected.

If the value of the **Network** key is specified, the prefixes "tcp:" and "np:" should not be specified. **Note:** You can force the use of TCP instead of shared memory, either by prefixing **tcp:** to the server name in the connection string, or by using **localhost**.| |Enclave Attestation Url|N/A|Gets or sets the enclave attestation Url to be used with enclave based Always Encrypted.| -|Encrypt|'false'|When `true`, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/dotnet/framework/data/adonet/connection-string-syntax).

Beginning in .NET Framework 4.5, when `TrustServerCertificate` is false and `Encrypt` is true, the server name (or IP address) in a SQL Server SSL certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see [Accepted wildcards used by server certificates for server authentication](https://support.microsoft.com/kb/258858).| +|Encrypt|'false'|When `true`, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/sql/connect/ado-net/connection-string-syntax).

Beginning in .NET Framework 4.5, when `TrustServerCertificate` is false and `Encrypt` is true, the server name (or IP address) in a SQL Server SSL certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see [Accepted wildcards used by server certificates for server authentication](https://support.microsoft.com/kb/258858).| |Enlist|'true'|`true` indicates that the SQL Server connection pooler automatically enlists the connection in the creation thread's current transaction context.| |Failover Partner|N/A|The name of the failover partner server where database mirroring is configured.

If the value of this key is "", then **Initial Catalog** must be present, and its value must not be "".

The server name can be 128 characters or less.

If you specify a failover partner but the failover partner server is not configured for database mirroring and the primary server (specified with the Server keyword) is not available, then the connection will fail.

If you specify a failover partner and the primary server is not configured for database mirroring, the connection to the primary server (specified with the Server keyword) will succeed if the primary server is available.| |Initial Catalog

-or-

Database|N/A|The name of the database.

The database name can be 128 characters or less.| |Integrated Security

-or-

Trusted_Connection|'false'|When `false`, User ID and Password are specified in the connection. When `true`, the current Windows account credentials are used for authentication.

Recognized values are `true`, `false`, `yes`, `no`, and `sspi` (strongly recommended), which is equivalent to `true`.

If User ID and Password are specified and Integrated Security is set to true, the User ID and Password will be ignored and Integrated Security will be used.

is a more secure way to specify credentials for a connection that uses SQL Server Authentication (`Integrated Security=false`).| |Max Pool Size|100|The maximum number of connections that are allowed in the pool.

Valid values are greater than or equal to 1. Values that are less than **Min Pool Size** generate an error.| |Min Pool Size|0|The minimum number of connections that are allowed in the pool.

Valid values are greater than or equal to 0. Zero (0) in this field means no minimum connections are initially opened.

Values that are greater than **Max Pool Size** generate an error.| -|Multiple Active Result Sets

-or-

MultipleActiveResultSets|false|When `true`, an application can maintain multiple active result sets (MARS). When `false`, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

Recognized values are `true` and `false`.

For more information, see [Multiple Active Result Sets (MARS)](/dotnet/framework/data/adonet/sql/multiple-active-result-sets-mars).| -|Multi Subnet Failover

-or-

MultiSubnetFailover|false|Always specify `multiSubnetFailover=True` when connecting to the availability group listener of a SQL Server 2012 (or later) availability group or a SQL Server 2012 (or later) Failover Cluster Instance. `multiSubnetFailover=True` configures SqlClient to provide faster detection of and connection to the (currently) active server. Possible values are `Yes` and `No`, `True` and `False` or `1` and `0`. For example:

`MultiSubnetFailover=True`

The default is `False`. For more information about SqlClient's support for Always On AGs, see [SqlClient Support for High Availability, Disaster Recovery](/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery).| +|Multiple Active Result Sets

-or-

MultipleActiveResultSets|false|When `true`, an application can maintain multiple active result sets (MARS). When `false`, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

Recognized values are `true` and `false`.

For more information, see [Multiple Active Result Sets (MARS)](/sql/connect/ado-net/sql/multiple-active-result-sets-mars).| +|Multi Subnet Failover

-or-

MultiSubnetFailover|false|Always specify `multiSubnetFailover=True` when connecting to the availability group listener of a SQL Server 2012 (or later) availability group or a SQL Server 2012 (or later) Failover Cluster Instance. `multiSubnetFailover=True` configures SqlClient to provide faster detection of and connection to the (currently) active server. Possible values are `Yes` and `No`, `True` and `False` or `1` and `0`. For example:

`MultiSubnetFailover=True`

The default is `False`. For more information about SqlClient's support for Always On AGs, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery).| |Network Library

-or-

Network

-or-

Net|N/A|The network library used to establish a connection to an instance of SQL Server. Supported values include:

dbnmpntw (Named Pipes)

dbmsrpcn (Multiprotocol, Windows RPC)

dbmsadsn (Apple Talk)

dbmsgnet (VIA)

dbmslpcn (Shared Memory)

dbmsspxn (IPX/SPX)

dbmssocn (TCP/IP)

Dbmsvinn (Banyan Vines)

The corresponding network DLL must be installed on the system to which you connect. If you do not specify a network and you use a local server (for example, "." or "(local)"), shared memory is used. In this example, the network library is Win32 Winsock TCP/IP (dbmssocn), and 1433 is the port being used.

`Network Library=dbmssocn;Data Source=000.000.000.000,1433;`| |Packet Size|8000|Size in bytes of the network packets used to communicate with an instance of SQL Server.

The packet size can be greater than or equal to 512 and less than or equal to 32768.| |Password

-or-

PWD|N/A|The password for the SQL Server account logging on. Not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keyword instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication.

The password must be 128 characters or less.| @@ -557,13 +557,13 @@ End Module |Replication|'false'|`true` if replication is supported using the connection.| |Transaction Binding|Implicit Unbind|Controls connection association with an enlisted `System.Transactions` transaction.

Possible values are:

`Transaction Binding=Implicit Unbind;`

`Transaction Binding=Explicit Unbind;`

Implicit Unbind causes the connection to detach from the transaction when it ends. After detaching, additional requests on the connection are performed in autocommit mode. The `System.Transactions.Transaction.Current` property is not checked when executing requests while the transaction is active. After the transaction has ended, additional requests are performed in autocommit mode.

If the system ends the transaction (in the scope of a using block) before the last command completes, it will throw .

Explicit Unbind causes the connection to remain attached to the transaction until the connection is closed or an explicit `SqlConnection.TransactionEnlist(null)` is called. Beginning in .NET Framework 4.0, changes to Implicit Unbind make Explicit Unbind obsolete. An `InvalidOperationException` is thrown if `Transaction.Current` is not the enlisted transaction or if the enlisted transaction is not active.| |Transparent Network IP Resolution

-or-

TransparentNetworkIPResolution|See description.|When the value of this key is set to `true`, the application is required to retrieve all IP addresses for a particular DNS entry and attempt to connect with the first one in the list. If the connection is not established within 0.5 seconds, the application will try to connect to all others in parallel. When the first answers, the application will establish the connection with the respondent IP address.

If the `MultiSubnetFailover` key is set to `true`, `TransparentNetworkIPResolution` is ignored.

If the `Failover Partner` key is set, `TransparentNetworkIPResolution` is ignored.

The value of this key must be `true`, `false`, `yes`, or `no`.

A value of `yes` is treated the same as a value of `true`.

A value of `no` is treated the same as a value of `false`.

The default values are as follows:

  • `false` when:

    • Connecting to Azure SQL Database where the data source ends with:

      • .database.chinacloudapi.cn
      • .database.usgovcloudapi.net
      • .database.cloudapi.de
      • .database.windows.net
    • `Authentication` is 'Active Directory Password' or 'Active Directory Integrated'
  • `true` in all other cases.
| -|Trust Server Certificate

-or-

TrustServerCertificate|'false'|When set to `true`, SSL is used to encrypt the channel when bypassing walking the certificate chain to validate trust. If TrustServerCertificate is set to `true` and Encrypt is set to `false`, the channel is not encrypted. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/dotnet/framework/data/adonet/connection-string-syntax).| +|Trust Server Certificate

-or-

TrustServerCertificate|'false'|When set to `true`, SSL is used to encrypt the channel when bypassing walking the certificate chain to validate trust. If TrustServerCertificate is set to `true` and Encrypt is set to `false`, the channel is not encrypted. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/sql/connect/ado-net/connection-string-syntax).| |Type System Version|N/A|A string value that indicates the type system the application expects. The functionality available to a client application is dependent on the version of SQL Server and the compatibility level of the database. Explicitly setting the type system version that the client application was written for avoids potential problems that could cause an application to break if a different version of SQL Server is used. **Note:** The type system version cannot be set for common language runtime (CLR) code executing in-process in SQL Server. For more information, see [SQL Server Common Language Runtime Integration](/dotnet/framework/data/adonet/sql/sql-server-common-language-runtime-integration).

Possible values are:

`Type System Version=SQL Server 2012;`

`Type System Version=SQL Server 2008;`

`Type System Version=SQL Server 2005;`

`Type System Version=Latest;`

`Type System Version=SQL Server 2012;` specifies that the application will require version 11.0.0.0 of Microsoft.SqlServer.Types.dll. The other `Type System Version` settings will require version 10.0.0.0 of Microsoft.SqlServer.Types.dll.

`Latest` is obsolete and should not be used. `Latest` is equivalent to `Type System Version=SQL Server 2008;`.| |User ID

-or-

UID

-or-|N/A|The SQL Server login account. Not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keywords instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication.

The user ID must be 128 characters or less.| |User Instance|'false'|A value that indicates whether to redirect the connection from the default SQL Server Express instance to a runtime-initiated instance running under the account of the caller.| |Workstation ID

-or-

WSID|The local computer name|The name of the workstation connecting to SQL Server.

The ID must be 128 characters or less.| - The following list contains the valid names for connection pooling values within the . For more information, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). + The following list contains the valid names for connection pooling values within the . For more information, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). - Connection Lifetime (or Load Balance Timeout) @@ -583,7 +583,7 @@ End Module > Universal data link (UDL) files are not supported for the .NET Framework Data Provider for SQL Server. > [!CAUTION] -> In this release, the application should use caution when constructing a connection string based on user input (for example when retrieving user ID and password information from a dialog box, and appending it to the connection string). The application should make sure that a user cannot embed additional connection string parameters in these values (for example, entering a password as "validpassword;database=somedb" in an attempt to attach to a different database). If you need to construct connection strings based on user input, use the new , which validates the connection string and helps to eliminate this problem. See [Connection String Builders](/dotnet/framework/data/adonet/connection-string-builders) for more information. +> In this release, the application should use caution when constructing a connection string based on user input (for example when retrieving user ID and password information from a dialog box, and appending it to the connection string). The application should make sure that a user cannot embed additional connection string parameters in these values (for example, entering a password as "validpassword;database=somedb" in an attempt to attach to a different database). If you need to construct connection strings based on user input, use the new , which validates the connection string and helps to eliminate this problem. See [Connection String Builders](/sql/connect/ado-net/connection-string-builders) for more information. @@ -712,7 +712,7 @@ End Module method to enlist in a distributed transaction. Because it enlists a connection in a instance, **EnlistTransaction** takes advantage of functionality available in the namespace for managing distributed transactions, making it preferable to **EnlistDistributedTransaction** for this purpose. For more information, see [Distributed Transactions](/dotnet/framework/data/adonet/distributed-transactions). + You can use the method to enlist in a distributed transaction. Because it enlists a connection in a instance, **EnlistTransaction** takes advantage of functionality available in the namespace for managing distributed transactions, making it preferable to **EnlistDistributedTransaction** for this purpose. For more information, see [Distributed Transactions](/sql/connect/ado-net/distributed-transactions). You can continue to enlist in an existing distributed transaction using the **EnlistDistributedTransaction** method if auto-enlistment is disabled. Enlisting in an existing distributed transaction makes sure that, if the transaction is committed or rolled back, modifications made by the code at the data source are also committed or rolled back. @@ -728,7 +728,7 @@ End Module method to enlist in a distributed transaction. Because it enlists a connection in a instance, **EnlistTransaction** takes advantage of functionality available in the namespace for managing distributed transactions, making it preferable to **EnlistDistributedTransaction**, which uses a **System.EnterpriseServices.ITransaction** object. It also has slightly different semantics: once a connection is explicitly enlisted on a transaction, it cannot be unenlisted or enlisted in another transaction until the first transaction finishes. For more information about distributed transactions, see [Distributed Transactions](/dotnet/framework/data/adonet/distributed-transactions). + You can use the method to enlist in a distributed transaction. Because it enlists a connection in a instance, **EnlistTransaction** takes advantage of functionality available in the namespace for managing distributed transactions, making it preferable to **EnlistDistributedTransaction**, which uses a **System.EnterpriseServices.ITransaction** object. It also has slightly different semantics: once a connection is explicitly enlisted on a transaction, it cannot be unenlisted or enlisted in another transaction until the first transaction finishes. For more information about distributed transactions, see [Distributed Transactions](/sql/connect/ado-net/distributed-transactions). ]]> @@ -746,7 +746,7 @@ End Module > [!NOTE] > An error with a severity level of 17 or above that causes the server to stop processing the command needs to be handled as an exception. In this case, an exception is thrown regardless of how the error is handled in the event. - For more information on working with events, see [Connection Events](/dotnet/framework/data/adonet/connection-events). For more information on errors generated by the SQL Server engine, see [Database Engine Errors](/sql/relational-databases/errors-events/database-engine-events-and-errors). + For more information on working with events, see [Connection Events](/sql/connect/ado-net/connection-events). For more information on errors generated by the SQL Server engine, see [Database Engine Errors](/sql/relational-databases/errors-events/database-engine-events-and-errors). ]]> @@ -763,7 +763,7 @@ End Module - Returns schema information for the data source of this . For more information about scheme, see [SQL Server Schema Collections](/dotnet/framework/data/adonet/sql-server-schema-collections). + Returns schema information for the data source of this . For more information about scheme, see [SQL Server Schema Collections](/sql/connect/ado-net/sql-server-schema-collections). A that contains schema information. To be added. @@ -890,7 +890,7 @@ GO The event occurs when a message with a severity of 10 or less is returned by SQL Server. Messages that have a severity between 11 and 20 raise an error and messages that have a severity over 20 causes the connection to close. For more information on SQL Server error levels, see [Database Engine Error Severities](/sql/relational-databases/errors-events/database-engine-error-severities). - For more information and an example, see [Connection Events](/dotnet/framework/data/adonet/connection-events). + For more information and an example, see [Connection Events](/sql/connect/ado-net/connection-events). ]]> @@ -988,7 +988,7 @@ GO A call to will attempt to cancel or close the corresponding call. - For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming). + For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming). ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml index 619ea2d690..e8075d81af 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml @@ -86,10 +86,10 @@ Integrated Security=True The supplied is not valid. - Declares the application workload type when connecting to a database in an SQL Server Availability Group. You can set the value of this property with . For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery). + Declares the application workload type when connecting to a database in an SQL Server Availability Group. You can set the value of this property with . For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery). Returns the current value of the property (a value of type ). To be added. - ADO.NET Overview + Overview of the SqlClient driver Gets or sets the name of the application associated with the connection string. @@ -158,7 +158,7 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security To set the value to null, use . Working with Connection Strings - ADO.NET Overview + Overview of the SqlClient driver Gets the authentication of the connection string. @@ -378,7 +378,7 @@ If `Multi Subnet Failover` is set to `true`, this setting has no effect. ]]> Working with Connection Strings - ADO.NET Overview + Overview of the SqlClient driver Gets or sets a Boolean value that indicates whether the SQL Server connection pooler automatically enlists the connection in the creation thread's current transaction context. @@ -451,7 +451,7 @@ If `Multi Subnet Failover` is set to `true`, this setting has no effect. in every case, because the supplies a fixed-size collection of key/value pairs. To be added. Working with Connection Strings - ADO.NET Overview + Overview of the SqlClient driver The key of the item to get or set. @@ -554,7 +554,7 @@ If `Multi Subnet Failover` is set to `true`, this setting has no effect. - If your application is connecting to an AlwaysOn availability group (AG) on different subnets, setting MultiSubnetFailover=true provides faster detection of and connection to the (currently) active server. For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery). + If your application is connecting to an AlwaysOn availability group (AG) on different subnets, setting MultiSubnetFailover=true provides faster detection of and connection to the (currently) active server. For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery). Returns indicating the current value of the property. To be added. @@ -892,7 +892,7 @@ Unable to retrieve value for null key. This property corresponds to the "User Instance" key within the connection string. > [!NOTE] -> This feature is available only with the SQL Server Express Edition. For more information on user instances, see [SQL Server Express User Instances](/dotnet/framework/data/adonet/sql/sql-server-express-user-instances). +> This feature is available only with the SQL Server Express Edition. For more information on user instances, see [SQL Server Express User Instances](/sql/connect/ado-net/sql/sql-server-express-user-instances). ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCredential.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCredential.xml index ce5709aea0..41caafa87f 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCredential.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCredential.xml @@ -14,7 +14,7 @@ to get or set a connection's object. Use to change the password for a object. For information on how a object affects connection pool behavior, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). + Use to get or set a connection's object. Use to change the password for a object. For information on how a object affects connection pool behavior, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). An exception will be raised if a non-null object is used in a connection with any of the following connection string keywords: @@ -68,19 +68,19 @@ using (SqlConnection conn = new SqlConnection(connString.ConnectionString)) ]]> - ADO.NET Overview + Overview of the SqlClient driver
Gets the password component of the object. The password component of the object. To be added. - ADO.NET Overview + Overview of the SqlClient driver Gets the user ID component of the object. The user ID component of the object. To be added. - ADO.NET Overview + Overview of the SqlClient driver diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml index 6d4ace23d3..6fb16341ef 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml @@ -7,7 +7,7 @@ , serves as a bridge between a and SQL Server for retrieving and saving data. The provides this bridge by mapping , which changes the data in the to match the data in the data source, and , which changes the data in the data source to match the data in the , using the appropriate Transact-SQL statements against the data source. The update is performed on a by-row basis. For every inserted, modified, and deleted row, the method determines the type of change that has been performed on it (`Insert`, `Update`, or `Delete`). Depending on the type of change, the `Insert`, `Update`, or `Delete` command template executes to propagate the modified row to the data source. When the fills a , it creates the necessary tables and columns for the returned data if they do not already exist. However, primary key information is not included in the implicitly created schema unless the property is set to . You may also have the create the schema of the , including primary key information, before filling it with data using `FillSchema`. For more information, see [Adding Existing Constraints to a DataSet](/dotnet/framework/data/adonet/adding-existing-constraints-to-a-dataset). + The , serves as a bridge between a and SQL Server for retrieving and saving data. The provides this bridge by mapping , which changes the data in the to match the data in the data source, and , which changes the data in the data source to match the data in the , using the appropriate Transact-SQL statements against the data source. The update is performed on a by-row basis. For every inserted, modified, and deleted row, the method determines the type of change that has been performed on it (`Insert`, `Update`, or `Delete`). Depending on the type of change, the `Insert`, `Update`, or `Delete` command template executes to propagate the modified row to the data source. When the fills a , it creates the necessary tables and columns for the returned data if they do not already exist. However, primary key information is not included in the implicitly created schema unless the property is set to . You may also have the create the schema of the , including primary key information, before filling it with data using `FillSchema`. For more information, see [Adding Existing Constraints to a DataSet](/sql/connect/ado-net/add-existing-constraints-to-dataset). is used in conjunction with and to increase performance when connecting to a SQL Server database. @@ -186,7 +186,7 @@ , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). + During , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). When is assigned to a previously created , the is not cloned. The maintains a reference to the previously created object. @@ -238,7 +238,7 @@ , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). + During , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). When is assigned to a previously created , the is not cloned. The maintains a reference to the previously created object. @@ -476,7 +476,7 @@ , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). + During , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). When is assigned to a previously created , the is not cloned. The maintains a reference to the previously created object. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml index f4459bdda9..8d83781431 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml @@ -334,7 +334,7 @@ |SqlInt64|SqlMoney|SqlSingle|SqlString| |String|UDT, which can be any CLR type marked with .||| - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -374,7 +374,7 @@ |SqlInt64|SqlMoney|SqlSingle|SqlString| |String|UDT, which can be any CLR type marked with .||| - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -833,7 +833,7 @@ - WriteTimeout - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -887,7 +887,7 @@ will raise an exception when used on an object returned by when is in effect. - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -982,7 +982,7 @@ will raise an exception when used on an object returned by when is in effect. - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -1059,7 +1059,7 @@ @@ -1126,7 +1126,7 @@ @@ -1168,7 +1168,7 @@ ## Remarks If the `behavior` parameter of is set to `Default`, reads the entire row before returning the Task. - For more information, including code samples, about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming). + For more information, including code samples, about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming). ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml index d4ecfc52c0..4e00d94732 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml @@ -14,7 +14,7 @@ > [!NOTE] > was designed to be used in ASP.NET or middle-tier services where there is a relatively small number of servers having dependencies active against the database. It was not designed for use in client applications, where hundreds or thousands of client computers would have objects set up for a single database server. If you are developing an application where you need reliable sub-second notifications when data changes, review the sections [Planning an Efficient Query Notifications Strategy](https://docs.microsoft.com/previous-versions/sql/sql-server-2008-r2/ms187528(v=sql.105)#planning-an-efficient-query-notifications-strategy) and [Alternatives to Query Notifications](https://docs.microsoft.com/previous-versions/sql/sql-server-2008-r2/ms187528(v=sql.105)#alternatives-to-query-notifications) in the [Planning for Notifications](https://docs.microsoft.com/previous-versions/sql/sql-server-2008-r2/ms187528(v%3dsql.105)) article. - For more information, see [Query Notifications in SQL Server](/dotnet/framework/data/adonet/sql/query-notifications-in-sql-server) and [Building Notification Solutions](https://docs.microsoft.com/previous-versions/sql/sql-server-2005/ms171065(v%3dsql.90)). + For more information, see [Query Notifications in SQL Server](/sql/connect/ado-net/sql/query-notifications-sql-server) and [Building Notification Solutions](https://docs.microsoft.com/previous-versions/sql/sql-server-2005/ms171065(v%3dsql.90)). > [!NOTE] > The event may be generated on a different thread from the thread that initiated command execution. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlException.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlException.xml index c5eafa53b1..9bb1ef18f3 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlException.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlException.xml @@ -105,7 +105,7 @@ catch (Exception ex) { - Represents the client connection ID. For more information, see Data Tracing in ADO.NET. + Represents the client connection ID. For more information, see Data Tracing in ADO.NET. The client connection ID. - Represents a parameter to a and optionally its mapping to columns. This class cannot be inherited. For more information on parameters, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). + Represents a parameter to a and optionally its mapping to columns. This class cannot be inherited. For more information on parameters, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). [!NOTE] > Nameless, also called ordinal, parameters are not supported by the .NET Framework Data Provider for SQL Server. - For more information, along with additional sample code demonstrating how to use parameters, see [Commands and Parameters](/dotnet/framework/data/adonet/commands-and-parameters). + For more information, along with additional sample code demonstrating how to use parameters, see [Commands and Parameters](/sql/connect/ado-net/commands-parameters). ## Examples - The following example creates multiple instances of through the collection within the . These parameters are used to select data from the data source and put the data in the . This example assumes that a and a have already been created by using the appropriate schema, commands, and connection. For more information and additional examples on using parameters, see [Retrieving and Modifying Data in ADO.NET](/dotnet/framework/data/adonet/retrieving-and-modifying-data) and [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). + The following example creates multiple instances of through the collection within the . These parameters are used to select data from the data source and put the data in the . This example assumes that a and a have already been created by using the appropriate schema, commands, and connection. For more information and additional examples on using parameters, see [Retrieving and Modifying Data in ADO.NET](/sql/connect/ado-net/retrieving-modifying-data) and [Configuring parameters](/sql/connect/ado-net/configure-parameters). [!code-csharp[SqlParameterCollection_Add6](~/../sqlclient/doc/samples/SqlParameterCollection_Add6.cs#1)] @@ -203,7 +203,7 @@ If you do not perform this conversion, the compiler assumes that you are trying ## Remarks The and are linked. Therefore, setting the changes the to a supporting . - For a list of the supported data types, see the appropriate member. For more information, see [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters). + For a list of the supported data types, see the appropriate member. For more information, see [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters). @@ -231,11 +231,11 @@ If you do not perform this conversion, the compiler assumes that you are trying ## Examples The following example creates a and sets some of its properties. - [Commands and Parameters](/dotnet/framework/data/adonet/commands-and-parameters) + [Commands and Parameters](/sql/connect/ado-net/commands-parameters) - [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters) + [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters) - [SQL Server and ADO.NET](/dotnet/framework/data/adonet/sql/) + [SQL Server and ADO.NET](/sql/connect/ado-net/sql/) ]]> @@ -428,7 +428,7 @@ static void CreateSqlParameterLocaleId(){ For fixed length data types, the value of is ignored. It can be retrieved for informational purposes, and returns the maximum amount of bytes the provider uses when transmitting the value of the parameter to the server. - For information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -449,7 +449,7 @@ static void CreateSqlParameterLocaleId(){ ## Remarks When is set to anything other than an empty string, the value of the parameter is retrieved from the column with the name. If is set to `Input`, the value is taken from the . If is set to `Output`, the value is taken from the data source. A of `InputOutput` is a combination of both. - For more information about how to use the property, see [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters) and [Updating Data Sources with DataAdapters](/dotnet/framework/data/adonet/updating-data-sources-with-dataadapters). + For more information about how to use the property, see [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters) and [Updating Data Sources with DataAdapters](/sql/connect/ado-net/update-data-sources-with-dataadapters). @@ -517,9 +517,9 @@ FieldName = @OriginalFieldName ## Remarks The and are linked. Therefore, setting the changes the to a supporting . - For a list of the supported data types, see the appropriate member. For more information, see [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters). + For a list of the supported data types, see the appropriate member. For more information, see [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters). - For information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -543,7 +543,7 @@ FieldName = @OriginalFieldName Use the property to return parameter values as common language runtime (CLR) types. - For information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -601,7 +601,7 @@ FieldName = @OriginalFieldName The property is overwritten by `SqlDataAdapter.UpdateCommand`. - For information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlParameterCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlParameterCollection.xml index 1141a8ec68..c75791c6bc 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlParameterCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlParameterCollection.xml @@ -9,7 +9,7 @@ ## Remarks If the command contains an ad hoc SQL statement, as opposed to a stored-procedure name, the number of the parameters in the collection must be equal to the number of parameter placeholders within the command text, or SQL Server raises an error. With a stored procedure, all the parameters declared in the stored procedure without a default value must be provided. Parameters declared with a default value are optional. This lets you specify a value other than the default. -For more information with additional sample code demonstrating how to use parameters, see [Commands and Parameters](/dotnet/framework/data/adonet/commands-and-parameters). +For more information with additional sample code demonstrating how to use parameters, see [Commands and Parameters](/sql/connect/ado-net/commands-parameters). diff --git a/doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml b/doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml index 1330327723..2397ba11a3 100644 --- a/doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml +++ b/doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml @@ -15,7 +15,7 @@ Specifying the FILESTREAM attribute on a `varbinary(max)` column causes SQL Serv The class is derived from the class, which represents an abstraction of a sequence of bytes from some arbitrary data source such as a file or a block of memory. You can read from a FILESTREAM by transferring data from a stream into a data structure such as an array of bytes. You can write to a FILESTREAM by transferring the data from a data structure into a stream. You can also seek within the stream, which allows you to query and modify data at the current position within the stream. -For conceptual documentation and code examples, see [FILESTREAM Data](/dotnet/framework/data/adonet/sql/filestream-data). +For conceptual documentation and code examples, see [FILESTREAM Data](/sql/connect/ado-net/sql/filestream-data). For documentation about setting up and configuring FILESTREAM data on SQL Server, see [Designing and Implementing FILESTREAM Storage](https://go.microsoft.com/fwlink/?LinkId=121499) in SQL Server 2008 Books Online. From 9268dcd35993514a18c67da433f33504d76cb86b Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Wed, 19 May 2021 19:03:54 -0700 Subject: [PATCH 43/87] Set ColumnEncryptionKeyCacheTtl to zero when key store provider is registered and used globally (#1078) --- .../netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs | 7 +++---- .../netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs | 7 +++---- .../src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs | 2 ++ .../tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 7e7ca226e7..5ce75ba598 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -98,8 +98,6 @@ private enum CultureCheckState : uint ///
private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; - private static string s_akvProviderName = "AZURE_KEY_VAULT"; - /// /// Dictionary object holding trusted key paths for various SQL Servers. /// Key to the dictionary is a SQL Server Name @@ -314,9 +312,10 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; - private static string s_akvProviderName = "AZURE_KEY_VAULT"; - /// Instance-level list of custom key store providers. It can be set more than once by the user. private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; @@ -149,9 +147,10 @@ static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary Date: Thu, 20 May 2021 03:04:19 +0000 Subject: [PATCH 44/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 26 +++++++++---------- .../netfx/src/Resources/Strings.es.resx | 26 +++++++++---------- .../netfx/src/Resources/Strings.fr.resx | 26 +++++++++---------- .../netfx/src/Resources/Strings.ja.resx | 26 +++++++++---------- .../netfx/src/Resources/Strings.ko.resx | 26 +++++++++---------- .../netfx/src/Resources/Strings.zh-Hans.resx | 26 +++++++++---------- 6 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index f42a330dd1..1e62706fa9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -4345,16 +4345,16 @@ Verwendete {0}-Instanz unterstützt die Spaltenverschlüsselung nicht. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Sie haben die Enclave-Nachweis-URL und das Nachweisprotokoll in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649  . - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Sie haben die Enclave-Nachweis-URL in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649  . - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Sie haben das Nachweisprotokoll in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649  . - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Sie haben die Enclave-Nachweis-URL in der Verbindungszeichenfolge angegeben, aber die SQL Server-Instanz hat keinen Enclave-Typ zurückgegeben. Stellen Sie sicher, dass der Enclave-Typ ordnungsgemäß in Ihrer Instanz konfiguriert wurde – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649  . {0} muss beim Durchführen von Batchupdates für alle Befehle ({1}, {2}, {3}, {4}) identisch sein. @@ -4459,7 +4459,7 @@ Das Ausführungstimeout ist abgelaufen. Der Timeoutzeitraum wurde überschritten, bevor der Vorgang beendet wurde, oder der Server antwortet nicht. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + Fehler bei der Überprüfung eines Nachweistokens. Die Tokensignatur stimmt nicht mit der Signatur überein, die anhand eines öffentlichen Schlüssels berechnet wurde, der vom Endpunkt für öffentliche Nachweisschlüssel unter „{0}“ abgerufen wurde. Überprüfen Sie die DNS-Zuordnung für den Endpunkt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649   . Wenn diese korrekt ist, wenden Sie sich an den Kundensupport. Interner Fehler beim erneuten Download des HGS-Stammzertifikats, nachdem bei der ersten Anforderung ein Fehler aufgetreten ist. Wenden Sie sich an den Kundensupport. @@ -4480,10 +4480,10 @@ Fehler bei der Überprüfung eines Nachweistokens. Das Token weisen ein ungültiges Format auf. Wenden Sie sich an den Kundensupport. Fehlerdetails: {0}. - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Der Nachweisdienst hat ein abgelaufenes HGS-Stammzertifikat für die Nachweis-URL „{0}“ zurückgegeben. Überprüfen Sie das für Ihre HGS-Instanz konfigurierte HGS-Stammzertifikat – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553   . - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + Das abgerufene HGS-Stammzertifikat für die Nachweis-URL „{0}“ weist ein ungültiges Format auf. Überprüfen Sie, ob die Nachweis-URL korrekt und der HGS-Server online und vollständig initialisiert sind – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553   . Wenden Sie sich an den Kundensupport, um weitere Informationen zu erhalten. Fehlerdetails: „{1}“. Fehler bei der Überprüfung eines Nachweistokens. Ein öffentlicher Schlüssel kann nicht aus dem Endpunkt für öffentliche Nachweisschlüssel abgerufen werden, oder der abgerufene Schlüssel weist ein ungültiges Format auf. Fehlerdetails: {0}. @@ -4501,31 +4501,31 @@ Fehler bei der Überprüfung des Nachweistokens während der Signaturüberprüfung. Ausnahme: {0}. - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + Fehler bei der Überprüfung eines Nachweistokens. Der Anspruch „{0}“ im Token weist einen ungültigen Wert von „{1}“ auf. Überprüfen Sie die Nachweisrichtlinie – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649   . Wenn die Richtlinie korrekt ist, wenden Sie sich an den Kundensupport. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + Fehler bei der Überprüfung des Nachweistokens. Der Anspruch „{0}“ fehlt im Token. Überprüfen Sie die Nachweisrichtlinie – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649   . Wenn die Richtlinie korrekt ist, wenden Sie sich an den Kundensupport. Es konnte nicht überprüft werden, ob die Enclave im Produktionsmodus ausgeführt wird. Wenden Sie sich an den Kundensupport. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Die Enclave-Richtlinie konnte aufgrund einer Abweichung zwischen den erwarteten und den tatsächlichen Werten der Richtlinie für die Eigenschaft „{0}“ nicht überprüft werden. Tatsächlich: „{1}“, erwartet: „{2}“ – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553   . - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + Fehler bei der Signaturüberprüfung des Enclave-Berichts. Die Berichtssignatur stimmt nicht mit der Signatur überein, die mit dem HGS-Stammzertifikat berechnet wurde. Überprüfen Sie die DNS-Zuordnung für den Endpunkt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553  . Wenn sie korrekt ist, wenden Sie sich an den Kundensupport. Der von SQL Server empfangene Enclavebericht weist nicht das richtige Format auf. Wenden Sie sich an den Kundensupport. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + Fehler beim Erstellen einer Vertrauenskette zwischen dem Integritätsbericht des Enclave-Hosts und dem HGS-Stammzertifikat für die Nachweis-URL „{0}“ mit dem Status: „{1}“ Überprüfen Sie, ob die Nachweis-URL mit der auf dem SQL Server konfigurierten URL übereinstimmt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553  . Wenn sowohl der Client als auch der SQL Server denselben Nachweisdienst verwenden, wenden Sie sich an den Kundensupport. Gibt ein Nachweisprotokoll für den entsprechenden Enclave-Nachweisdienst an. - Specifies an IP address preference when connecting to SQL instances. + Gibt eine bevorzugte IP-Adresse beim Herstellen einer Verbindung mit SQL-Instanzen an. Der vom Server zurückgegebene Enclave-Typ "{0}" wird nicht unterstützt. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index c46cac7c77..a448f5271f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -4345,16 +4345,16 @@ La instancia de {0} en uso no admite el cifrado de columnas. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Ha especificado la dirección URL de atestación de enclave y el protocolo de atestación en la cadena de conexión, pero la instancia de SQL Server que se está usando no admite los cálculos basados en enclave. Vea https://go.microsoft.com/fwlink/?linkid=2157649 para obtener más detalles. - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Ha especificado el vínculo de atestación enclave en la cadena de conexión, pero la instancia de SQL Server que se está usando no admite los cálculos basados en enclave. Vea https://go.microsoft.com/fwlink/?linkid=2157649 para obtener más detalles. - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Ha especificado el protocolo de atestación en la cadena de conexión, pero la instancia de SQL Server que se está usando no admite los cálculos basados en enclave. Vea https://go.microsoft.com/fwlink/?linkid=2157649 para obtener más detalles. - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Ha especificado la dirección URL de atestación de enclave en la cadena de conexión, pero SQL Server no ha devuelto un tipo de enclave. Asegúrese de que el tipo de enclave está configurado correctamente en la instancia. Vea https://go.microsoft.com/fwlink/?linkid=2157649 para obtener más detalles. {0} debería ser idéntico en todos los comandos ({1}, {2}, {3}, {4}) cuando se ejecutan actualizaciones en lote. @@ -4459,7 +4459,7 @@ Se agotó el tiempo de espera de ejecución. El período de tiempo de espera transcurrió antes de la finalización de la operación o el servidor no responde. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + Error en la validación de un token de atestación. La firma del token no coincide con la firma calculada mediante una clave pública recuperada del punto de conexión de clave pública de atestación en "{0}". Compruebe la asignación de DNS del punto de conexión. Vea https://go.microsoft.com/fwlink/?linkid=2157649 para obtener más detalles. Si es correcta, póngase en contacto con el servicio de atención al cliente. Se ha producido un error interno al reintentar la descarga del certificado raíz de HGS después de un error en la solicitud inicial. Póngase en contacto con el servicio de atención al cliente. @@ -4480,10 +4480,10 @@ Error en la validación de un token de atestación. El token tiene un formato no válido. Póngase en contacto con el servicio de atención al cliente. Detalles del error: "{0}". - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + El servicio de atestación devolvió un certificado raíz de HGS expirado para la dirección URL de atestación "{0}". Compruebe el certificado raíz de HGS configurado para su instancia de HGS. Vea https://go.microsoft.com/fwlink/?linkid=2160553 para obtener más detalles. - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + El certificado raíz de HGS obtenido para la dirección URL de atestación "{0}" tiene un formato no válido. Compruebe que la dirección URL de atestación sea correcta y que el servidor HGS esté en línea y completamente inicializado. Vea https://go.microsoft.com/fwlink/?linkid=2160553 para obtener más información. Para obtener soporte técnico adicional, póngase en contacto con los servicios de atención al cliente. Detalles del error: "{1}". Error en la validación de un token de atestación. No se puede recuperar una clave pública del punto de conexión de clave pública de atestación o la clave recuperada tiene un formato no válido. Detalles del error: "{0}". @@ -4501,31 +4501,31 @@ Error en la validación del token de atestación durante la validación de la firma. Excepción: "{0}". - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + Error en la validación de un token de atestación. La notificación "{0}" en el token tiene un valor no válido para "{1}". Compruebe la directiva de atestación. Vea https://go.microsoft.com/fwlink/?linkid=2157649 para obtener más detalles. Si ka directiva es correcta, póngase en contacto con el servicio de atención al cliente. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + Error en la validación del token de atestación. Falta la notificación "{0}" en el token. Compruebe la directiva de atestación. Vea https://go.microsoft.com/fwlink/?linkid=2157649 para obtener más detalles. Si ka directiva es correcta, póngase en contacto con el servicio de atención al cliente. No se pudo comprobar si el enclave se está ejecutando en modo de producción. Póngase en contacto con el servicio de atención al cliente. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + No se pudo comprobar la directiva de enclave debido a una diferencia entre los valores reales y esperados de la directiva en la propiedad "{0}". Valores reales: "{1}"; valores esperados: "{2}". Vea https://go.microsoft.com/fwlink/?linkid=2160553 para obtener más detalles. - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + Error de comprobación de firma del informe de enclave. La firma del informe no coincide con la firma calculada mediante el certificado raíz de HGS. Compruebe la asignación de DNS del punto de conexión. Vea https://go.microsoft.com/fwlink/?linkid=2160553 para más detalles. Si es correcta, póngase en contacto con el servicio de atención al cliente. El informe de enclave recibido desde SQL Server no tiene el formato correcto. Póngase en contacto con el servicio de atención al cliente. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + No se pudo compilar una cadena de confianza entre el informe de mantenimiento del host del enclave y el certificado raíz de HGS para la dirección URL de atestación "{0}" con el estado "{1}". Compruebe que la dirección URL de atestación coincida con la configurada en SQL Server. Vea https://go.microsoft.com/fwlink/?linkid=2160553 para obtener más detalles. Si el cliente y SQL Server usan el mismo servicio de atestación, póngase en contacto con los servicios de atención al cliente. Especifica un protocolo de atestación para el servicio de atestación de enclave correspondiente. - Specifies an IP address preference when connecting to SQL instances. + Especifica una preferencia de dirección IP al conectarse a las instancias de SQL. No se admite el tipo de enclave "{0}" que ha devuelto el servidor. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index 3d44d53917..1c198907a5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -4345,16 +4345,16 @@ L’instance {0} utilisée ne prend pas en charge le chiffrement de colonne. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Vous avez spécifié l'URL d'attestation et le protocole d'attestation de l'enclave dans la chaîne de connexion, alors que le SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour  plus  d’informations. - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Vous avez spécifié l'URL de l'attestation d'enclave dans la chaîne de connexion, mais le SQL Server utilisé ne prend pas en charge les calculs basés sur les enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour  plus  d’informations. - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Vous avez spécifié l'attestation de l'enclave dans la chaîne de connexion, alors que le SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour  plus  d’informations. - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + Vous avez spécifié l'URL d'attestation d'enclave dans la chaîne de connexion, mais SQL Server n'a pas retourné de type d'enclave. Vérifiez que le type d'enclave est correctement configuré dans votre instance.- consultez  https://go.microsoft.com/fwlink/?linkid=2157649 pour plus  d’informations. {0} doit être identique dans toutes les commandes ({1}, {2}, {3}, {4}) pendant la réalisation de mises à jour par lot. @@ -4459,7 +4459,7 @@ Le délai d'exécution a expiré. Le délai d'attente s'est écoulé avant la fin de l'opération ou le serveur ne répond pas. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + La validation d'un jeton d'attestation a échoué. La signature du jeton ne correspond pas à la signature calculée à l'aide d'une clé publique récupérée sur le point de terminaison de clé publique d'attestation dans « {0} ». Vérifiez le mappage DNS pour le point de terminaison - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour plus d’informations. S'il est correct, contactez le service client. Une erreur interne s'est produite pendant la nouvelle tentative de téléchargement du certificat racine SGH après l'échec de la demande initiale. Contactez le service client. @@ -4480,10 +4480,10 @@ La validation d'un jeton d'attestation a échoué. Le jeton a un format non valide. Contactez le service client. Détails de l'erreur : « {0} ». - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Le service d'attestation a retourné un certificat racine SGH expiré pour l'URL d'attestation « {0} ». Vérifiez le certificat racine SGH configuré pour votre instance SGH - consultez https://go.microsoft.com/fwlink/?linkid=2160553 pour plus d’informations. - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + Le certificat racine SGH obtenu pour l'URL d'attestation « {0} » a un format non valide. Vérifiez que l'URL d'attestation est correcte et que le serveur SGH est en ligne et complètement initialisé - consultez https://go.microsoft.com/fwlink/?linkid=2160553 pour plus d’informations. Pour une assistance supplémentaire, contactez le service client. Détails de l'erreur : « {1} ». La validation d'un jeton d'attestation a échoué. Impossible de récupérer une clé publique sur le point de terminaison de clé publique d'attestation, ou la clé récupérée a un format non valide. Détails de l'erreur : « {0} ». @@ -4501,31 +4501,31 @@ La validation du jeton d'attestation a échoué pendant la validation de la signature. Exception : « {0} ». - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + La validation d'un jeton d'attestation a échoué. La revendication « {0} » dans le jeton a la valeur non valide « {1} ». Vérifiez la stratégie d'attestation - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour plus d’informations. Si la stratégie est correcte, contactez le service client. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + La validation du jeton d'attestation a échoué. La revendication « {0} » est manquante dans le jeton. Vérifiez la stratégie d'attestation - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour plus d’informations. Si la stratégie est correcte, contactez le service client. La vérification de l'exécution de l'enclave en mode de production a échoué. Contactez le service client. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + Impossible de vérifier la stratégie de l'enclave en raison d'une différence entre la valeur attendue et la valeur réelle de la stratégie sur la propriété '{0}'. Valeur réelle : '{1}', valeur attendue : '{2}' - consultez https://go.microsoft.com/fwlink/?linkid=2160553 pour plus de détails. - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + La vérification de signature du rapport d'enclave a échoué. La signature de rapport ne correspond pas à la signature calculée à l'aide du certificat racine SGH - consultez https://go.microsoft.com/fwlink/?linkid=2160553 pour plus d’informations. Vérifiez le mappage DNS pour le point de terminaison. S'il est correct, contactez le service client. Le rapport d'enclave envoyé par SQL Server n'a pas un format correct. Contactez le service client. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + Échec de la génération d'une chaîne d'approbation entre le rapport d'intégrité de l'hôte d'enclave et le certificat racine SGH pour l'URL d'attestation « {0} » avec l'état « {1} ». Vérifiez que l'URL d'attestation correspond à l'URL configurée sur le SQL Server - consultez https://go.microsoft.com/fwlink/?linkid=2160553 pour plus d’informations.Si le client et le serveur SQL utilisent le même service d'attestation, contactez le service client. Spécifie un protocole d'attestation pour le service d'attestation d'enclave correspondant. - Specifies an IP address preference when connecting to SQL instances. + Spécifie une préférence d'adresse IP lors de la connexion aux instances SQL. Le type d'enclave « {0} » retourné par le serveur n'est pas pris en charge. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index 596df25dcd..b24c73bc44 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -4345,16 +4345,16 @@ 使用中の {0} インスタンスは列暗号化をサポートしていません。 - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 接続文字列の中でエンクレーブ構成証明の URL と構成証明プロトコルが指定されていますが、使用中の SQL Server ではエンクレーブに基づく計算はサポートされていません。詳細については、https://go.microsoft.com/fwlink/?linkid=2157649 を参照してください。 - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 接続文字列の中でエンクローブ構成証明の URLが指定されていますが、使用中の SQL Server ではエンクレーブに基づく計算はサポートされていません。詳細については、https://go.microsoft.com/fwlink/?linkid=2157649 を参照してください。 - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 接続文字列の中で構成証明プロトコルが指定されていますが、使用中の SQL Server ではエンクレーブに基づく計算はサポートされていません。詳細については、https://go.microsoft.com/fwlink/?linkid=2157649 を参照してください。 - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 接続文字列の中でエンクレーブ構成証明 URL が指定されていますが、SQL Server からはエンクレーブ型が返されませんでした。お使いのインスタンスでエンクレーブ型が正しく構成されていることをご確認ください。詳細については、https://go.microsoft.com/fwlink/?linkid=2157649 を参照してください。 バッチ更新を実行する際は、{0} を全コマンド ({1}、{2}、{3}、{4}) 上で一致させる必要があります。 @@ -4459,7 +4459,7 @@ 実行タイムアウトの期限が切れました。操作完了前にタイムアウト期間が過ぎたか、サーバーが応答していません。 - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + 構成証明トークンの検証に失敗しました。トークンの署名が、'{0}' の構成証明の公開キー エンドポイントから取得した公開キーを使用して計算された署名と一致しません。エンドポイントの DNS マッピングをご確認ください。詳細については、https://go.microsoft.com/fwlink/?linkid=2157649 を参照してください。適切である場合、カスタマー サポート サービスにお問い合わせください。 最初の要求が失敗した後に、HGS ルート証明書のダウンロードを再試行しているときに内部エラーが発生しました。カスタマー サポート サービスにお問い合わせください。 @@ -4480,10 +4480,10 @@ 構成証明トークンの検証に失敗しました。トークンの形式が無効です。カスタマー サポート サービスにお問い合わせください。エラーの詳細: '{0}'。 - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + 構成証明サービスにより、構成証明 URL '{0}' の期限切れの HGS ルート証明書が返されました。HGS インスタンス用に構成されている HGS ルート証明書をご確認ください。詳細については、https://go.microsoft.com/fwlink/?linkid=2160553 を参照してください。 - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + 構成証明 URL '{0}' に関する、取得された HGS ルート証明書の形式が無効です。構成証明 URL が正しいこと、および HGS サーバーがオンラインであり完全に初期化されていることをご確認ください。詳細については、https://go.microsoft.com/fwlink/?linkid=2160553 を参照してください。追加のサポートについては、カスタマー サポート サービスにお問い合わせください。エラーの詳細: '{1}'。 構成証明トークンの検証に失敗しました。構成証明の公開キー エンドポイントから公開キーを取得できないか、取得したキーの形式が無効です。エラーの詳細: '{0}'。 @@ -4501,31 +4501,31 @@ 署名の検証中に、構成証明トークンの検証に失敗しました。例外: '{0}'。 - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + 構成証明トークンの検証に失敗しました。トークン内の要求 '{0}' の値 '{1}' が無効です。構成証明ポリシーをご確認ください。詳細については、https://go.microsoft.com/fwlink/?linkid=2157649 を参照してください。ポリシーが適切である場合、カスタマー サポート サービスにお問い合わせください。 - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + 構成証明トークンの検証に失敗しました。要求 '{0}' がトークンにありません。構成証明ポリシーをご確認ください。詳細については、https://go.microsoft.com/fwlink/?linkid=2157649 を参照してください。ポリシーが適切である場合、カスタマー サポート サービスにお問い合わせください。 エンクレーブが運用モードで実行されているかどうかを確認できませんでした。カスタマー サポート サービスにお問い合わせください。 - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + プロパティ '{0}' のポリシーの必要な値と実際の値の違いにより、エンクレーブ ポリシーを確認できませんでした。実際の値: '{1}'、必要な値: '{2}'。詳細については、https://go.microsoft.com/fwlink/?linkid=2160553 を参照してください。 - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + エンクレーブ レポートの署名の検証に失敗しました。レポートの署名が、HGS ルート証明書を使用して計算された署名と一致しません。エンドポイントの DNS マッピングをご確認ください。詳細については、https://go.microsoft.com/fwlink/?linkid=2160553 を参照してください。適切である場合、カスタマー サポート サービスにお問い合わせください。 SQL Server から受信したエンクレーブ レポートの形式が適切ではありません。カスタマー サポート サービスにお問い合わせください。 - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + エンクレーブ ホストの正常性レポートと構成証明 URL '{0}' の HGS ルート証明書との間で信頼チェーンを構築できませんでした。状態は '{1}' です。構成証明 URL が SQL Server で構成されている URL と一致していることをご確認ください。詳細については、https://go.microsoft.com/fwlink/?linkid=2160553 を参照してください。クライアントと SQL Server の両方で同じ構成証明サービスが使用されている場合は、カスタマー サポート サービスにお問い合わせください。 対応するエンクレーブ構成証明サービスに対して構成証明プロトコルを指定します。 - Specifies an IP address preference when connecting to SQL instances. + SQL インスタンスに接続するときの IP アドレスの設定を指定します。 サーバーから返されたエンクレーブの種類 '{0}' はサポートされていません。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index be6e6e3a08..4d630c1f02 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -4345,16 +4345,16 @@ 사용 중인 {0} 인스턴스에서 열 암호화를 지원하지 않습니다. - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 연결 문자열에 enclave 증명 URL 및 증명 프로토콜을 지정했지만 사용 중인 SQL Server가 enclave 기반 계산을 지원하지 않습니다. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2157649를 참조 하세요. - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 연결 문자열에 enclave 증명 URL을 지정했지만 사용 중인 SQL Server가 enclave 기반 계산을 지원하지 않습니다. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2157649를 참조 하세요. - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 연결 문자열에 증명 프로토콜을 지정했지만 사용 중인 SQL Server가 enclave 기반 계산을 지원하지 않습니다. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2157649를 참조 하세요. - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 연결 문자열에 enclave 증명 URL을 지정했지만 SQL Server는 enclave 형식을 반환하지 않았습니다. enclave 형식이 인스턴스에 올바르게 구성되어 있는지 확인하세요. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2157649를 확인 하세요. 일괄 업데이트를 수행할 때 {0}이(가) 모든 명령({1}, {2}, {3}, {4})에서 동일해야 합니다. @@ -4459,7 +4459,7 @@ 실행 제한 시간을 초과했습니다. 작업이 완료되기 전에 실행 제한 시간이 지났거나 서버가 응답하지 않습니다. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + 증명 토큰의 유효성을 검사하지 못했습니다. 토큰 서명이 '{0}'의 증명 공개 키 엔드포인트에서 검색된 공개 키를 사용하여 계산된 서명과 일치하지 않습니다. 엔드포인트에 대한 DNS 매핑을 확인하세요. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2157649 을 확인 하세요. 올바른 경우 고객 지원 서비스에 문의하세요. 초기 요청이 실패한 후 HGS 루트 인증서 다운로드를 다시 시도하는 동안 내부 오류가 발생했습니다. 고객 지원 서비스에 문의하세요. @@ -4480,10 +4480,10 @@ 증명 토큰의 유효성을 검사하지 못했습니다. 토큰의 형식이 유효하지 않습니다. 고객 지원 서비스에 문의하세요. 오류 세부 정보: '{0}'. - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + 증명 서비스가 증명 URL '{0}'에 대해 만료된 HGS 루트 인증서를 반환했습니다. HGS 인스턴스에 대해 구성된 HGS 루트 인증서를 확인하세요. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2160553 을 확인 하세요. - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + 증명 URL '{0}'에 대해 가져온 HGS 루트 인증서의 형식이 유효하지 않습니다. 증명 URL이 올바르며 HGS 서버가 온라인 상태이고 완전히 초기화되었는지 확인하세요. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2160553 을 확인 하세요. 추가 지원은 고객 지원 서비스 팀에 문의하세요. 오류 세부 정보: '{1}'. 증명 토큰의 유효성을 검사하지 못했습니다. 증명 공개 키 엔드포인트에서 공개 키를 검색할 수 없거나, 검색된 키의 형식이 유효하지 않습니다. 오류 세부 정보: '{0}'. @@ -4501,31 +4501,31 @@ 서명 유효성 검사 도중 증명 토큰의 유효성을 검사하지 못했습니다. 예외: '{0}'. - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + 증명 토큰의 유효성을 검사하지 못했습니다. 토큰의 클레임 '{0}'의 '{1}'의 값이 유효하지 않습니다. 증명 정책을 확인하세요. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2157649 을 확인 하세요. 정책이 올바르면 고객 지원 서비스에 문의하세요. - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + 증명 토큰의 유효성을 검사하지 못했습니다. 클레임 '{0}'이(가) 토큰에 없습니다. 증명 정책을 확인하세요. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2157649 을 확인 하세요. 정책이 올바르면 고객 지원 서비스에 문의하세요. enclave가 프로덕션 모드에서 실행되고 있는지 확인하지 못했습니다. 고객 지원 서비스에 문의하세요. - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + 속성 '{0}'의 예상 값과 실제 값 간의 차이로 인해 enclave 정책을 확인할 수 없습니다. 실제: '{1}', 예상: '{2}'. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2160553 을 확인 하세요. - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + enclave 보고서의 서명을 확인하지 못했습니다. 보고서 서명이 HGS 루트 인증서를 사용하여 계산된 서명과 일치하지 않습니다. 엔드포인트에 대한 DNS 매핑을 확인하세요. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2160553을 확인 하세요. 올바른 경우 고객 지원 서비스에 문의하세요. SQL Server에서 수신된 enclave 보고서의 형식이 잘못되었습니다. 고객 지원 서비스에 문의하세요. - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + 상태가 '{0}' 인 증명 URL '{1}'에 대한 HGS 루트 인증서와 enclave 호스트의 상태 보고서 간에 신뢰 체인을 만들지 못했습니다. 증명 URL이 SQL Server에 구성된 URL과 일치하는지 확인하세요. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2160553을 확인 하세요. 클라이언트와 SQL Server 모두 동일한 증명 서비스를 사용하는 경우 고객 지원 서비스 팀에 문의하세요. 해당하는 enclave 증명 서비스에 대한 증명 프로토콜을 지정합니다. - Specifies an IP address preference when connecting to SQL instances. + SQL 인스턴스에 연결할 때 IP 주소 기본 설정을 지정합니다. 서버에서 반환된 enclave 형식 '{0}'은(는) 지원되지 않습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 42b7f9c362..38fa7ec36f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -4345,16 +4345,16 @@ 使用的 {0} 实例不支持列加密。 - You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 你已在连接字符串中指定 enclave 证明 URL 和证明协议,但使用中的 SQL Server 不支持基于 enclave 的计算 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2157649。 - You have specified the enclave attestation URL in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 你已在连接字符串中指定 enclave 证明 URL,但使用中的 SQL Server 不支持基于 enclave 的计算 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2157649。 - You have specified the attestation protocol in the connection string, but the SQL Server in use does not support enclave based computations - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 你已在连接字符串中指定证明协议,但使用中的 SQL Server 不支持基于 enclave 的计算 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2157649。 - You have specified the enclave attestation URL in the connection string, but the SQL Server did not return an enclave type. Please make sure the enclave type is correctly configured in your instance - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. + 你已在连接字符串中指定 enclave 证明 URL,但 SQL Server 未返回 enclave 类型。请确保在实例中正确配置 enclave 类型 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2157649。 执行批更新时,{0} 的执行方式应与所有命令({1}、{2}、{3}、{4})的执行方式相同。 @@ -4459,7 +4459,7 @@ 执行超时已过期。完成操作之前已超时或服务器未响应。 - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If correct, contact Customer Support Services. + 无法验证证明令牌。令牌签名与使用从“{0}”处的证明公钥终结点检索的公钥计算的签名不匹配。请验证终结点的 DNS 映射 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2157649 。 初始请求失败后,重试下载 HGS 根证书时出现内部错误。请联系客户支持服务。 @@ -4480,10 +4480,10 @@ 无法验证证明令牌。令牌的格式无效。请联系客户支持服务。错误详细信息:“{0}”。 - The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + 证明服务为证明 URL“{0}”返回的 HGS 根证书已到期。请检查为你的 HGS 实例配置的 HGS 根证书 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2160553 。 - The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. For additional support contact Customer Support Services. Error details: '{1}'. + 为证明 URL“{0}”获取的 HGS 根证书的格式无效。请验证证明 URL 是否正确,且 HGS 服务器是否处于联机状态且已完全初始化 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2160553 。有关其他支持,请联系客户支持服务。错误详细信息: “{1}”。 无法验证证明令牌。无法从证明公钥终结点检索公钥,或检索到的密钥的格式无效。错误详细信息:“{0}”。 @@ -4501,31 +4501,31 @@ 无法在签名验证期间验证证明令牌。异常:“{0}”。 - The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + 无法验证证明令牌。令牌中的声明“{0}”具有无效值“{1}”。请验证证明策略 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2157649 。如果策略正确,请联系客户支持服务。 - The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy - see https://go.microsoft.com/fwlink/?linkid=2157649 for more details. If the policy is correct, contact Customer Support Services. + 无法验证证明令牌。令牌中缺少声明“{0}”。请验证证明策略 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2157649 。如果策略正确,请联系客户支持服务。 无法检查 enclave 是否在生产模式下运行。请联系客户支持服务。 - Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}' - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. + 无法验证 enclave 策略,因为属性“{0}”上策略的预期值和实际值不一致。实际值:“{1}”,预期值:“{2}”- 有关 更多 详细信息, 请参阅 https://go.microsoft.com/fwlink/?linkid=2160553 。 - Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If correct, contact Customer Support Services. + 无法验证 enclave 报告的签名。报告签名与使用 HGS 根证书计算出的签名不匹配。请验证终结点的 DNS 映射 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2160553。如果正确,请联系客户支持服务。 从 SQL Server 收到的 enclave 报告的格式不正确。请联系客户支持服务。 - Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server - see https://go.microsoft.com/fwlink/?linkid=2160553 for more details. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + 无法在 enclave 主机的运行状况报告和证明 URL“{0}”的 HGS 根证书之间生成信任链,状态为“{1}”。请验证证明 URL 与 SQL Server 上配置的 URL 是否匹配 - 有关 更多 详细信息,请 参阅 https://go.microsoft.com/fwlink/?linkid=2160553。如果客户端和 SQL Server 使用的证明服务相同,请联系客户支持服务。 为其相应的 enclave 证明服务指定证明协议。 - Specifies an IP address preference when connecting to SQL instances. + 指定连接到 SQL 实例时的 IP 地址首选项。 不支持从服务器返回的 enclave 类型“{0}”。 From 3c140f8c9559c1d74950e44e21be35f68d38e0c8 Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Thu, 20 May 2021 19:59:28 +0000 Subject: [PATCH 45/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 24 +++++++++---------- .../netfx/src/Resources/Strings.fr.resx | 8 +++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 1e62706fa9..8e68b802c9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -4345,16 +4345,16 @@ Verwendete {0}-Instanz unterstützt die Spaltenverschlüsselung nicht. - Sie haben die Enclave-Nachweis-URL und das Nachweisprotokoll in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649  . + Sie haben die Enclave-Nachweis-URL und das Nachweisprotokoll in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen –Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649 - Sie haben die Enclave-Nachweis-URL in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649  . + Sie haben die Enclave-Nachweis-URL in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649. - Sie haben das Nachweisprotokoll in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649  . + Sie haben das Nachweisprotokoll in der Verbindungszeichenfolge angegeben, aber die verwendete SQL Server-Instanz unterstützt keine Enclave-basierten Berechnungen – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649. - Sie haben die Enclave-Nachweis-URL in der Verbindungszeichenfolge angegeben, aber die SQL Server-Instanz hat keinen Enclave-Typ zurückgegeben. Stellen Sie sicher, dass der Enclave-Typ ordnungsgemäß in Ihrer Instanz konfiguriert wurde – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649  . + Sie haben die Enclave-Nachweis-URL in der Verbindungszeichenfolge angegeben, aber die SQL Server-Instanz hat keinen Enclave-Typ zurückgegeben. Stellen Sie sicher, dass der Enclave-Typ ordnungsgemäß in Ihrer Instanz konfiguriert wurde –Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649. {0} muss beim Durchführen von Batchupdates für alle Befehle ({1}, {2}, {3}, {4}) identisch sein. @@ -4459,7 +4459,7 @@ Das Ausführungstimeout ist abgelaufen. Der Timeoutzeitraum wurde überschritten, bevor der Vorgang beendet wurde, oder der Server antwortet nicht. - Fehler bei der Überprüfung eines Nachweistokens. Die Tokensignatur stimmt nicht mit der Signatur überein, die anhand eines öffentlichen Schlüssels berechnet wurde, der vom Endpunkt für öffentliche Nachweisschlüssel unter „{0}“ abgerufen wurde. Überprüfen Sie die DNS-Zuordnung für den Endpunkt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649   . Wenn diese korrekt ist, wenden Sie sich an den Kundensupport. + Fehler bei der Überprüfung eines Nachweistokens. Die Tokensignatur stimmt nicht mit der Signatur überein, die anhand eines öffentlichen Schlüssels berechnet wurde, der vom Endpunkt für öffentliche Nachweisschlüssel unter „{0}“ abgerufen wurde. Überprüfen Sie die DNS-Zuordnung für den Endpunkt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649. Wenn diese korrekt ist, wenden Sie sich an den Kundensupport. Interner Fehler beim erneuten Download des HGS-Stammzertifikats, nachdem bei der ersten Anforderung ein Fehler aufgetreten ist. Wenden Sie sich an den Kundensupport. @@ -4480,10 +4480,10 @@ Fehler bei der Überprüfung eines Nachweistokens. Das Token weisen ein ungültiges Format auf. Wenden Sie sich an den Kundensupport. Fehlerdetails: {0}. - Der Nachweisdienst hat ein abgelaufenes HGS-Stammzertifikat für die Nachweis-URL „{0}“ zurückgegeben. Überprüfen Sie das für Ihre HGS-Instanz konfigurierte HGS-Stammzertifikat – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553   . + Der Nachweisdienst hat ein abgelaufenes HGS-Stammzertifikat für die Nachweis-URL „{0}“ zurückgegeben. Überprüfen Sie das für Ihre HGS-Instanz konfigurierte HGS-Stammzertifikat – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553. - Das abgerufene HGS-Stammzertifikat für die Nachweis-URL „{0}“ weist ein ungültiges Format auf. Überprüfen Sie, ob die Nachweis-URL korrekt und der HGS-Server online und vollständig initialisiert sind – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553   . Wenden Sie sich an den Kundensupport, um weitere Informationen zu erhalten. Fehlerdetails: „{1}“. + Das abgerufene HGS-Stammzertifikat für die Nachweis-URL „{0}“ weist ein ungültiges Format auf. Überprüfen Sie, ob die Nachweis-URL korrekt und der HGS-Server online und vollständig initialisiert sind – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553. Wenden Sie sich an den Kundensupport, um weitere Informationen zu erhalten. Fehlerdetails: „{1}“. Fehler bei der Überprüfung eines Nachweistokens. Ein öffentlicher Schlüssel kann nicht aus dem Endpunkt für öffentliche Nachweisschlüssel abgerufen werden, oder der abgerufene Schlüssel weist ein ungültiges Format auf. Fehlerdetails: {0}. @@ -4501,25 +4501,25 @@ Fehler bei der Überprüfung des Nachweistokens während der Signaturüberprüfung. Ausnahme: {0}. - Fehler bei der Überprüfung eines Nachweistokens. Der Anspruch „{0}“ im Token weist einen ungültigen Wert von „{1}“ auf. Überprüfen Sie die Nachweisrichtlinie – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649   . Wenn die Richtlinie korrekt ist, wenden Sie sich an den Kundensupport. + Fehler bei der Überprüfung eines Nachweistokens. Der Anspruch „{0}“ im Token weist einen ungültigen Wert von „{1}“ auf. Überprüfen Sie die Nachweisrichtlinie – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649. Wenn die Richtlinie korrekt ist, wenden Sie sich an den Kundensupport. - Fehler bei der Überprüfung des Nachweistokens. Der Anspruch „{0}“ fehlt im Token. Überprüfen Sie die Nachweisrichtlinie – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649   . Wenn die Richtlinie korrekt ist, wenden Sie sich an den Kundensupport. + Fehler bei der Überprüfung des Nachweistokens. Der Anspruch „{0}“ fehlt im Token. Überprüfen Sie die Nachweisrichtlinie – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2157649. Wenn die Richtlinie korrekt ist, wenden Sie sich an den Kundensupport. Es konnte nicht überprüft werden, ob die Enclave im Produktionsmodus ausgeführt wird. Wenden Sie sich an den Kundensupport. - Die Enclave-Richtlinie konnte aufgrund einer Abweichung zwischen den erwarteten und den tatsächlichen Werten der Richtlinie für die Eigenschaft „{0}“ nicht überprüft werden. Tatsächlich: „{1}“, erwartet: „{2}“ – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553   . + Die Enclave-Richtlinie konnte aufgrund einer Abweichung zwischen den erwarteten und den tatsächlichen Werten der Richtlinie für die Eigenschaft „{0}“ nicht überprüft werden. Tatsächlich: „{1}“, erwartet: „{2}“ – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553. - Fehler bei der Signaturüberprüfung des Enclave-Berichts. Die Berichtssignatur stimmt nicht mit der Signatur überein, die mit dem HGS-Stammzertifikat berechnet wurde. Überprüfen Sie die DNS-Zuordnung für den Endpunkt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553  . Wenn sie korrekt ist, wenden Sie sich an den Kundensupport. + Fehler bei der Signaturüberprüfung des Enclave-Berichts. Die Berichtssignatur stimmt nicht mit der Signatur überein, die mit dem HGS-Stammzertifikat berechnet wurde. Überprüfen Sie die DNS-Zuordnung für den Endpunkt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553. Wenn sie korrekt ist, wenden Sie sich an den Kundensupport. Der von SQL Server empfangene Enclavebericht weist nicht das richtige Format auf. Wenden Sie sich an den Kundensupport. - Fehler beim Erstellen einer Vertrauenskette zwischen dem Integritätsbericht des Enclave-Hosts und dem HGS-Stammzertifikat für die Nachweis-URL „{0}“ mit dem Status: „{1}“ Überprüfen Sie, ob die Nachweis-URL mit der auf dem SQL Server konfigurierten URL übereinstimmt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553  . Wenn sowohl der Client als auch der SQL Server denselben Nachweisdienst verwenden, wenden Sie sich an den Kundensupport. + Fehler beim Erstellen einer Vertrauenskette zwischen dem Integritätsbericht des Enclave-Hosts und dem HGS-Stammzertifikat für die Nachweis-URL „{0}“ mit dem Status: „{1}“ Überprüfen Sie, ob die Nachweis-URL mit der auf dem SQL Server konfigurierten URL übereinstimmt – Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2160553. Wenn sowohl der Client als auch der SQL Server denselben Nachweisdienst verwenden, wenden Sie sich an den Kundensupport. Gibt ein Nachweisprotokoll für den entsprechenden Enclave-Nachweisdienst an. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index 1c198907a5..b9538f5095 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -4345,16 +4345,16 @@ L’instance {0} utilisée ne prend pas en charge le chiffrement de colonne. - Vous avez spécifié l'URL d'attestation et le protocole d'attestation de l'enclave dans la chaîne de connexion, alors que le SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour  plus  d’informations. + Vous avez spécifié l'URL d'attestation et le protocole d'attestation de l'enclave dans la chaîne de connexion, alors que le SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour plus d’informations. - Vous avez spécifié l'URL de l'attestation d'enclave dans la chaîne de connexion, mais le SQL Server utilisé ne prend pas en charge les calculs basés sur les enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour  plus  d’informations. + Vous avez spécifié l'URL de l'attestation d'enclave dans la chaîne de connexion, mais le SQL Server utilisé ne prend pas en charge les calculs basés sur les enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour plus d’informations. - Vous avez spécifié l'attestation de l'enclave dans la chaîne de connexion, alors que le SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour  plus  d’informations. + Vous avez spécifié l'attestation de l'enclave dans la chaîne de connexion, alors que le SQL Server utilisée ne prend pas en charge les calculs basés sur des enclaves - consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour plus d’informations. - Vous avez spécifié l'URL d'attestation d'enclave dans la chaîne de connexion, mais SQL Server n'a pas retourné de type d'enclave. Vérifiez que le type d'enclave est correctement configuré dans votre instance.- consultez  https://go.microsoft.com/fwlink/?linkid=2157649 pour plus  d’informations. + Vous avez spécifié l'URL d'attestation d'enclave dans la chaîne de connexion, mais SQL Server n'a pas retourné de type d'enclave. Vérifiez que le type d'enclave est correctement configuré dans votre instance.- consultez https://go.microsoft.com/fwlink/?linkid=2157649 pour plus d’informations. {0} doit être identique dans toutes les commandes ({1}, {2}, {3}, {4}) pendant la réalisation de mises à jour par lot. From 294c452069f21464473842f757734b62c9ed6b4d Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 20 May 2021 16:15:39 -0700 Subject: [PATCH 46/87] Release notes for v3.0.0-preview3 (#1079) --- CHANGELOG.md | 19 +++ release-notes/3.0/3.0.0-preview3.md | 176 ++++++++++++++++++++++++++++ release-notes/3.0/3.0.md | 1 + release-notes/3.0/README.md | 1 + 4 files changed, 197 insertions(+) create mode 100644 release-notes/3.0/3.0.0-preview3.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e59feab7c3..8449fe774d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +## [Preview Release 3.0.0-preview3.21140.5] - 2021-05-20 + +### Added + +- Added support for "Active Directory Default" authentication mode [#1043](https://github.com/dotnet/SqlClient/pull/1043) +- Added support for connection-level and command-level registration of custom key store providers to enable multi-tenant applications to control key store access [#1045](https://github.com/dotnet/SqlClient/pull/1045) [#1056](https://github.com/dotnet/SqlClient/pull/1056) [#1078](https://github.com/dotnet/SqlClient/pull/1078) +- Added IP address preference support for TCP connections [#1015](https://github.com/dotnet/SqlClient/pull/1015) + +### Fixed + +- Fixed corrupted connection issue when an exception occurs during RPC execution with TVP types [#1068](https://github.com/dotnet/SqlClient/pull/1068) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1042](https://github.com/dotnet/SqlClient/pull/1042) + +### Changed + +- Updated error messages for enclave exceptions to include a link to a troubleshooting guide. [#994](https://github.com/dotnet/SqlClient/pull/994) +- Changes to share common files between projects [#1022](https://github.com/dotnet/SqlClient/pull/1022) [#1038](https://github.com/dotnet/SqlClient/pull/1038) [#1040](https://github.com/dotnet/SqlClient/pull/1040) [#1033](https://github.com/dotnet/SqlClient/pull/1033) [#1028](https://github.com/dotnet/SqlClient/pull/1028) [#1039](https://github.com/dotnet/SqlClient/pull/1039) + + ## [Preview Release 3.0.0-preview2.21106.5] - 2021-04-16 ### Breaking Changes over preview release v3.0.0-preview1 diff --git a/release-notes/3.0/3.0.0-preview3.md b/release-notes/3.0/3.0.0-preview3.md new file mode 100644 index 0000000000..6c213d0c3f --- /dev/null +++ b/release-notes/3.0/3.0.0-preview3.md @@ -0,0 +1,176 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.0.0-preview3.21140.5 released 20 May 2021 + +This update brings the below changes over the previous release: + +### Added + +- Added support for "Active Directory Default" authentication mode [#1043](https://github.com/dotnet/SqlClient/pull/1043) [Read more](#active-directory-default-authentication-support) +- Added support for connection-level and command-level registration of custom key store providers to enable multi-tenant applications to control key store access [#1045](https://github.com/dotnet/SqlClient/pull/1045) [#1056](https://github.com/dotnet/SqlClient/pull/1056) [#1078](https://github.com/dotnet/SqlClient/pull/1078) [Read more](#custom-master-key-store-provider-registration-enhancements) +- Added IP address preference support for TCP connections [#1015](https://github.com/dotnet/SqlClient/pull/1015) [Read more](#ip-address-preference) + +### Fixed + +- Fixed corrupted connection issue when an exception occurs during RPC execution with TVP types [#1068](https://github.com/dotnet/SqlClient/pull/1068) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1042](https://github.com/dotnet/SqlClient/pull/1042) + +### Changed + +- Updated error messages for enclave exceptions to include a link to a troubleshooting guide. [#994](https://github.com/dotnet/SqlClient/pull/994) +- Changes to share common files between projects [#1022](https://github.com/dotnet/SqlClient/pull/1022) [#1038](https://github.com/dotnet/SqlClient/pull/1038) [#1040](https://github.com/dotnet/SqlClient/pull/1040) [#1033](https://github.com/dotnet/SqlClient/pull/1033) [#1028](https://github.com/dotnet/SqlClient/pull/1028) [#1039](https://github.com/dotnet/SqlClient/pull/1039) + +### Active Directory Default authentication support + +This PR introduces a new SQL Authentication method, **Active Directory Default**. This authentication mode widens the possibilities of user authentication, extending login solutions to the client environment, Visual Studio Code, Visual Studio, Azure CLI etc. + +With this authentication mode, the driver acquires a token by passing "[DefaultAzureCredential](https://docs.microsoft.com/dotnet/api/azure.identity.defaultazurecredential)" from the Azure Identity library to acquire an access token. This mode attempts to use these credential types to acquire an access token in the following order: + +- **EnvironmentCredential** + - Enables authentication to Azure Active Directory using client and secret, or username and password, details configured in the following environment variables: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_CLIENT_CERTIFICATE_PATH, AZURE_USERNAME, AZURE_PASSWORD ([More details](https://docs.microsoft.com/dotnet/api/azure.identity.environmentcredential)) +- **ManagedIdentityCredential** + - Attempts authentication to Azure Active Directory using a managed identity that has been assigned to the deployment environment. **"Client Id" of "User Assigned Managed Identity"** is read from the **"User Id" connection property**. +- **SharedTokenCacheCredential** + - Authenticates using tokens in the local cache shared between Microsoft applications. +- **VisualStudioCredential** + - Enables authentication to Azure Active Directory using data from Visual Studio +- **VisualStudioCodeCredential** + - Enables authentication to Azure Active Directory using data from Visual Studio Code. +- **AzureCliCredential** + - Enables authentication to Azure Active Directory using Azure CLI to obtain an access token. + +> InteractiveBrowserCredential is disabled in the driver implementation of "Active Directory Default", and "Active Directory Interactive" is the only option available to acquire a token using MFA/Interactive authentication.* + +> Further customization options are not available at the moment. + +### Custom master key store provider registration enhancements + +Microsoft.Data.SqlClient now offers more control of where master key store providers are accessible in an application in order to better support multi-tenant applications and their use of column encryption/decryption. The following APIs are introduced to allow registration of custom master key store providers on instances of `SqlConnection` and `SqlCommand`: + +```cs +public class SqlConnection +{ + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) +} +public class SqlCommand +{ + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(IDictionary customProviders) +} +``` + +The static API on `SqlConnection`, i.e. `SqlConnection.RegisterColumnEncryptionKeyStoreProviders` to register custom master key store providers globally continues to be supported. The column encryption key cache maintained globally only applies to globally registered providers. + +#### Column master key store provider registration precedence + +The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. No providers should be registered on the connection or command instances if one of the built-in column master key store providers is needed. + +Custom master key store providers can be registered with the driver at three different layers. The global level is as it currently is. The new per-connection and per-command level registrations will be empty initially and can be set more than once. + +The precedence of the three registrations are as follows: + +- The per-command registration will be checked if it is not empty. +- If the per-command registration is empty, the per-connection registration will be checked if it is not empty. +- If the per-connection registration is empty, the global registration will be checked. + +Once any key store provider is found at a registration level, the driver will **NOT** fall back to the other registrations to search for a provider. If providers are registered but the proper provider is not found at a level, an exception will be thrown containing only the registered providers in the registration that was checked. + +#### Column encryption key cache precedence + +The column encryption keys (CEKs) for custom key store providers registered using the new instance-level APIs will not be cached by the driver. The key store providers need to implement their own cache to gain performance. This local cache of column encryption keys implemented by custom key store providers will be disabled by the driver if the key store provider instance is registered in the driver at the global level. + +A new API has also been introduced on the `SqlColumnEncryptionKeyStoreProvider` base class to set the cache time to live: + +```cs +public abstract class SqlColumnEncryptionKeyStoreProvider +{ + // The default value of Column Encryption Key Cache Time to Live is 0. + // Provider's local cache is disabled for globally registered providers. + // Custom key store provider implementation must include column encryption key cache to provide caching support to locally registered providers. + public virtual TimeSpan? ColumnEncryptionKeyCacheTtl { get; set; } = new TimeSpan(0); +} +``` + +### IP Address preference + +A new connection property `IPAddressPreference` is introduced to specify the IP address family preference to the driver when establishing TCP connections. If `Transparent Network IP Resolution` (in .NET Framework) or `Multi Subnet Failover` is set to `true`, this setting has no effect. Below are the three accepted values for this property: + +- **IPv4First** + - This is the default preference value. The driver will use resolved IPv4 addresses first. If none of them can be connected to successfully, it will try resolved IPv6 addresses. + +- **IPv6First** + - The driver will use resolved IPv6 addresses first. If none of them can be connected to successfully, it will try resolved IPv4 addresses. + +- **UsePlatformDefault** + - The driver will try IP addresses in the order received from the DNS resolution response. + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework 4.6.1 + +- Microsoft.Data.SqlClient.SNI 3.0.0-preview1.21104.2 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/3.0/3.0.md b/release-notes/3.0/3.0.md index 7dff6ce7de..48948b83b5 100644 --- a/release-notes/3.0/3.0.md +++ b/release-notes/3.0/3.0.md @@ -4,5 +4,6 @@ The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2021/05/20 | 3.0.0-preview3.21140.5 | [release notes](3.0.0-preview3.md) | | 2021/04/15 | 3.0.0-preview2.21106.5 | [release notes](3.0.0-preview2.md) | | 2021/03/15 | 3.0.0-preview1.21075.2 | [release notes](3.0.0-preview1.md) | diff --git a/release-notes/3.0/README.md b/release-notes/3.0/README.md index 7dff6ce7de..48948b83b5 100644 --- a/release-notes/3.0/README.md +++ b/release-notes/3.0/README.md @@ -4,5 +4,6 @@ The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2021/05/20 | 3.0.0-preview3.21140.5 | [release notes](3.0.0-preview3.md) | | 2021/04/15 | 3.0.0-preview2.21106.5 | [release notes](3.0.0-preview2.md) | | 2021/03/15 | 3.0.0-preview1.21075.2 | [release notes](3.0.0-preview1.md) | From b1c9067ec4a4b6b89a724d4e0aaf1403d8cff71b Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Thu, 20 May 2021 16:22:39 -0700 Subject: [PATCH 47/87] Control building tools separately (#1077) --- build.proj | 11 ++++++++++- tools/targets/NotSupported.targets | 8 +------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/build.proj b/build.proj index 763e975df1..449bef821a 100644 --- a/build.proj +++ b/build.proj @@ -9,6 +9,8 @@ src\NuGet.config Debug AnyCPU + + true true false @@ -55,7 +57,7 @@ - + @@ -77,6 +79,13 @@ + + + $(DotNetCmd) dotnet build -c Release" + + + + diff --git a/tools/targets/NotSupported.targets b/tools/targets/NotSupported.targets index a074b3369b..e9f44cbeee 100644 --- a/tools/targets/NotSupported.targets +++ b/tools/targets/NotSupported.targets @@ -1,6 +1,5 @@ - true @@ -21,7 +20,6 @@ Inputs: * A contract assembly * Reference assemblies - Generates source for the contract that throws PlatformNotSupportedException --> "%(ResolvedMatchingContract.Identity)" $(GenAPIArgs) -l:"@(_referencePathDirectories)" $(GenAPIArgs) -o:"$(NotSupportedSourceFile)" - $(DotNetCmd) dotnet build -c Release" $(GenAPIArgs) -t:"$(GeneratePlatformNotSupportedAssemblyMessage)" $(GenAPIArgs) -global "$(DotNetCmd) $(ToolsArtifactsDir)netcoreapp2.1\Microsoft.DotNet.GenAPI.dll" "$(ToolsArtifactsDir)net472\Microsoft.DotNet.GenAPI.exe" - - - - + From f58464be1bb8ffb15f979d34ecfcd24a7807a476 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 21 May 2021 11:15:23 -0700 Subject: [PATCH 48/87] Release notes for v2.1.3 (#1080) --- CHANGELOG.md | 7 ++++ release-notes/2.1/2.1.3.md | 77 +++++++++++++++++++++++++++++++++++++ release-notes/2.1/2.1.md | 1 + release-notes/2.1/README.md | 1 + 4 files changed, 86 insertions(+) create mode 100644 release-notes/2.1/2.1.3.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8449fe774d..95ed0bc4f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +## [Stable Release 2.1.3] - 2021-05-21 + +### Fixed + +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set [#1051](https://github.com/dotnet/SqlClient/pull/1051) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1049](https://github.com/dotnet/SqlClient/pull/1049) + ## [Preview Release 3.0.0-preview3.21140.5] - 2021-05-20 ### Added diff --git a/release-notes/2.1/2.1.3.md b/release-notes/2.1/2.1.3.md new file mode 100644 index 0000000000..5034cfa306 --- /dev/null +++ b/release-notes/2.1/2.1.3.md @@ -0,0 +1,77 @@ +# Release Notes + +## Microsoft.Data.SqlClient 2.1.3 released 21 May 2021 + +This update brings the below changes over the previous stable release: + +### Fixed + +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set [#1051](https://github.com/dotnet/SqlClient/pull/1051) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1049](https://github.com/dotnet/SqlClient/pull/1049) + +### Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/2.1/2.1.md b/release-notes/2.1/2.1.md index 5a2cd356c6..50d02afa7e 100644 --- a/release-notes/2.1/2.1.md +++ b/release-notes/2.1/2.1.md @@ -4,6 +4,7 @@ The following Microsoft.Data.SqlClient 2.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2021/05/21 | 2.1.3 | [release notes](2.1.3.md) | | 2021/03/03 | 2.1.2 | [release notes](2.1.2.md) | | 2020/12/18 | 2.1.1 | [release notes](2.1.1.md) | | 2020/11/19 | 2.1.0 | [release notes](2.1.0.md) | diff --git a/release-notes/2.1/README.md b/release-notes/2.1/README.md index 5a2cd356c6..50d02afa7e 100644 --- a/release-notes/2.1/README.md +++ b/release-notes/2.1/README.md @@ -4,6 +4,7 @@ The following Microsoft.Data.SqlClient 2.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2021/05/21 | 2.1.3 | [release notes](2.1.3.md) | | 2021/03/03 | 2.1.2 | [release notes](2.1.2.md) | | 2020/12/18 | 2.1.1 | [release notes](2.1.1.md) | | 2020/11/19 | 2.1.0 | [release notes](2.1.0.md) | From 4d1194b4d409f87e1fd917202cd1947043954714 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 25 May 2021 15:42:37 -0700 Subject: [PATCH 49/87] Include missing refs (#1083) --- .../netcore/ref/Microsoft.Data.SqlClient.cs | 2 ++ .../netfx/ref/Microsoft.Data.SqlClient.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index ed3f0b3c73..80069a220f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -473,6 +473,8 @@ public abstract partial class SqlColumnEncryptionKeyStoreProvider public virtual byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { throw null; } /// public virtual bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { throw null; } + /// + public virtual System.TimeSpan? ColumnEncryptionKeyCacheTtl { get { throw null; } set { } } } /// public enum SqlCommandColumnEncryptionSetting diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index eac679eece..9b77d05d7e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -506,6 +506,8 @@ public abstract partial class SqlColumnEncryptionKeyStoreProvider public virtual byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { throw null; } /// public virtual bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { throw null; } + /// + public virtual System.TimeSpan? ColumnEncryptionKeyCacheTtl { get { throw null; } set { } } } /// [System.ComponentModel.DefaultEventAttribute("RecordsAffected")] From 5c5a15d5ac842b48c4e99bff951b026f07f1a5d3 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Tue, 25 May 2021 15:42:52 -0700 Subject: [PATCH 50/87] Update LanguageString.cs (#1081) --- .../tests/tools/TDS/TDS/LanguageString.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/LanguageString.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/LanguageString.cs index a96751c9ee..cec2fe4c61 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/LanguageString.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/LanguageString.cs @@ -16,7 +16,7 @@ public static class LanguageString private const string French = "Fran\u00E7ais"; private const string Japanese = "\u65E5\u672C\u8A9E"; private const string Danish = "Dansk"; - private const string Spanish = "Espa\u00F1o"; + private const string Spanish = "Espa\u00F1ol"; private const string Italian = "Italiano"; private const string Dutch = "Nederlands"; private const string Norwegian = "Norsk"; From f3515917c053c3ee6783beaff3901df5c0f0dfe6 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Thu, 27 May 2021 16:40:38 -0700 Subject: [PATCH 51/87] Update API docs (#1085) --- .../SqlColumnEncryptionKeyStoreProvider.xml | 9 ++++++- .../Microsoft.Data.SqlClient/SqlCommand.xml | 16 +++++++++++- .../SqlConnection.xml | 25 ++++++++++++++++--- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml index 2ea4a2522a..435fbc6c15 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml @@ -60,7 +60,14 @@ The Gets or sets the lifespan of the decrypted column encryption key in the cache. Once the timespan has elapsed, the decrypted column encryption key is discarded and must be revalidated. - Internally, there is a cache of column encryption keys (once they are decrypted). This is useful for rapidly decrypting multiple data values. The default value is 2 hours. Setting this value to zero disables caching. + + . Any caching implementation should reference the value of this property before caching a column encryption key and not cache it if the value is zero. This will avoid duplicate caching and possible user confusion when they are trying to configure key caching. + ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index 399797cb56..1bc3c94f48 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -2782,11 +2782,25 @@ You must set the value for this property before the command is executed for it t -or- - An EncryptionKeyStoreProvider value in the dictionary was null. + A value in the dictionary was null. A string key in the dictionary started with "MSSQL_". This prefix is reserved for system providers. + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index d7da726364..81b4e407fe 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -529,7 +529,7 @@ End Module |Asynchronous Processing

-or-

Async|'false'|This property is obsolete and should not used.

When `true`, enables asynchronous operation support. Recognized values are `true`, `false`, `yes`, and `no`.

This property is ignored beginning in .NET Framework 4.5. For more information about SqlClient support for asynchronous programming, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming).| |AttachDBFilename

-or-

Extended Properties

-or-

Initial File Name|N/A|The name of the primary database file, including the full path name of an attachable database. AttachDBFilename is only supported for primary data files with an .mdf extension.

If the value of the AttachDBFileName key is specified in the connection string, the database is attached and becomes the default database for the connection.

If this key is not specified and if the database was previously attached, the database will not be reattached. The previously attached database will be used as the default database for the connection.

If this key is specified together with the AttachDBFileName key, the value of this key will be used as the alias. However, if the name is already used in another attached database, the connection will fail.

The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string. **Note:** Remote server, HTTP, and UNC path names are not supported.

The database name must be specified with the keyword 'database' (or one of its aliases) as in the following:

"AttachDbFileName=|DataDirectory|\data\YourDB.mdf;integrated security=true;database=YourDatabase"

An error will be generated if a log file exists in the same directory as the data file and the 'database' keyword is used when attaching the primary data file. In this case, remove the log file. Once the database is attached, a new log file will be automatically generated based on the physical path.| |Attestation Protocol|N/A|Gets or sets the value of Attestation Protocol.

Valid values are:
`AAS`
`HGS`| -|Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).

Valid values are:

`Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, `Sql Password`. Currently `Active Directory Integrated` and `Active Directory Interactive` modes of authentication are supported only for .NET Framework. | +|Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).

Valid values are:

`Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, 'Active Directory Service Principal', 'Active Directory Device Code Flow', 'Active Directory Managed Identity', 'Active Directory MSI', 'Active Directory Default', `Sql Password`. Currently `Active Directory Integrated` and `Active Directory Interactive` modes of authentication are supported only for .NET Framework. | |Column Encryption Setting|N/A|Enables or disables [Always Encrypted](/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-2017) functionality for the connection.| |Command Timeout|30|The default wait time (in seconds) before terminating the attempt to execute a command and generating an error.

Valid values are greater than or equal to 0 and less than or equal to 2147483647.| |Connect Timeout

-or-

Connection Timeout

-or-

Timeout|15|The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error.

Valid values are greater than or equal to 0 and less than or equal to 2147483647.

When opening a connection to a Azure SQL Database, set the connection timeout to 30 seconds.| @@ -544,6 +544,7 @@ End Module |Failover Partner|N/A|The name of the failover partner server where database mirroring is configured.

If the value of this key is "", then **Initial Catalog** must be present, and its value must not be "".

The server name can be 128 characters or less.

If you specify a failover partner but the failover partner server is not configured for database mirroring and the primary server (specified with the Server keyword) is not available, then the connection will fail.

If you specify a failover partner and the primary server is not configured for database mirroring, the connection to the primary server (specified with the Server keyword) will succeed if the primary server is available.| |Initial Catalog

-or-

Database|N/A|The name of the database.

The database name can be 128 characters or less.| |Integrated Security

-or-

Trusted_Connection|'false'|When `false`, User ID and Password are specified in the connection. When `true`, the current Windows account credentials are used for authentication.

Recognized values are `true`, `false`, `yes`, `no`, and `sspi` (strongly recommended), which is equivalent to `true`.

If User ID and Password are specified and Integrated Security is set to true, the User ID and Password will be ignored and Integrated Security will be used.

is a more secure way to specify credentials for a connection that uses SQL Server Authentication (`Integrated Security=false`).| +|IP Address Preference

-or-

IPAddressPreference|IPv4First|The IP address family preference when establishing TCP connections. If `Transparent Network IP Resolution` (in .NET Framework) or `Multi Subnet Failover` is set to true, this setting has no effect. Supported values include:

`IPAddressPreference=IPv4First`

`IPAddressPreference=IPv6First`

`IPAddressPreference=UsePlatformDefault`| |Max Pool Size|100|The maximum number of connections that are allowed in the pool.

Valid values are greater than or equal to 1. Values that are less than **Min Pool Size** generate an error.| |Min Pool Size|0|The minimum number of connections that are allowed in the pool.

Valid values are greater than or equal to 0. Zero (0) in this field means no minimum connections are initially opened.

Values that are greater than **Max Pool Size** generate an error.| |Multiple Active Result Sets

-or-

MultipleActiveResultSets|false|When `true`, an application can maintain multiple active result sets (MARS). When `false`, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

Recognized values are `true` and `false`.

For more information, see [Multiple Active Result Sets (MARS)](/sql/connect/ado-net/sql/multiple-active-result-sets-mars).| @@ -1022,7 +1023,11 @@ GO Dictionary of custom column encryption key store providers - Registers the column encryption key store providers. This function should only be called once in an app. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + + Registers the column encryption key store providers. This function should only be called once in an app. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + + The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. No providers should be registered on the connection or command instances if one of the built-in column master key store providers is needed. + value in the dictionary was null. A string key in the dictionary started with "MSSQL_". This prefix is reserved for system providers. + + + Gets or sets a value that specifies the From 0578fbf5772296cde19c8a9a2b21f876b1facc8d Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 31 May 2021 14:21:52 -0700 Subject: [PATCH 52/87] Add ConfigurationManager dependency to nuspec (#1087) --- ...crosoft.Data.SqlClient.ManualTesting.Tests.csproj | 12 ++++++------ tools/props/Versions.props | 1 - tools/specs/Microsoft.Data.SqlClient.nuspec | 3 +++ 3 files changed, 9 insertions(+), 7 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 0a1946d7cc..d70b91d864 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 @@ -294,22 +294,22 @@ - - - - + + + + + - - + diff --git a/tools/props/Versions.props b/tools/props/Versions.props index e9f424add2..99b015718c 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -67,7 +67,6 @@ 5.0.0-beta.20206.4 2.0.8 161.41011.9 - 5.0.0 $(NugetPackageVersion) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index c03b550e12..d8df8ccf98 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -33,6 +33,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -72,6 +73,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -85,6 +87,7 @@ When using NuGet 3.x this package requires at least version 3.4. + From ae2d7313f77f38d03ed1dbffe3a71ce2f3d45891 Mon Sep 17 00:00:00 2001 From: David Engel Date: Tue, 1 Jun 2021 15:27:16 -0700 Subject: [PATCH 53/87] Tweak event counter display info (#1091) --- .../SqlClientEventSource.NetCoreApp.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs index 1cab291611..c0312ca219 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs @@ -67,14 +67,14 @@ protected override void EventCommandMethodCall(EventCommandEventArgs command) _activeHardConnections = _activeHardConnections ?? new PollingCounter("active-hard-connections", this, () => _activeHardConnectionsCounter) { - DisplayName = "Actual active connections are made to servers", + DisplayName = "Actual active connections currently made to servers", DisplayUnits = "count" }; _hardConnectsPerSecond = _hardConnectsPerSecond ?? new IncrementingPollingCounter("hard-connects", this, () => _hardConnectsCounter) { - DisplayName = "Actual connections are made to servers", + DisplayName = "Actual connection rate to servers", DisplayUnits = "count / sec", DisplayRateTimeScale = TimeSpan.FromSeconds(1) }; @@ -82,7 +82,7 @@ protected override void EventCommandMethodCall(EventCommandEventArgs command) _hardDisconnectsPerSecond = _hardDisconnectsPerSecond ?? new IncrementingPollingCounter("hard-disconnects", this, () => _hardDisconnectsCounter) { - DisplayName = "Actual disconnections are made to servers", + DisplayName = "Actual disconnection rate from servers", DisplayUnits = "count / sec", DisplayRateTimeScale = TimeSpan.FromSeconds(1) }; @@ -90,14 +90,14 @@ protected override void EventCommandMethodCall(EventCommandEventArgs command) _activeSoftConnections = _activeSoftConnections ?? new PollingCounter("active-soft-connects", this, () => _activeSoftConnectionsCounter) { - DisplayName = "Active connections got from connection pool", + DisplayName = "Active connections retrieved from the connection pool", DisplayUnits = "count" }; _softConnects = _softConnects ?? new IncrementingPollingCounter("soft-connects", this, () => _softConnectsCounter) { - DisplayName = "Connections got from connection pool", + DisplayName = "Rate of connections retrieved from the connection pool", DisplayUnits = "count / sec", DisplayRateTimeScale = TimeSpan.FromSeconds(1) }; @@ -105,7 +105,7 @@ protected override void EventCommandMethodCall(EventCommandEventArgs command) _softDisconnects = _softDisconnects ?? new IncrementingPollingCounter("soft-disconnects", this, () => _softDisconnectsCounter) { - DisplayName = "Connections returned to the connection pool", + DisplayName = "Rate of connections returned to the connection pool", DisplayUnits = "count / sec", DisplayRateTimeScale = TimeSpan.FromSeconds(1) }; @@ -113,15 +113,15 @@ protected override void EventCommandMethodCall(EventCommandEventArgs command) _numberOfNonPooledConnections = _numberOfNonPooledConnections ?? new PollingCounter("number-of-non-pooled-connections", this, () => _nonPooledConnectionsCounter) { - DisplayName = "Number of connections are not using connection pooling", - DisplayUnits = "count / sec" + DisplayName = "Number of connections not using connection pooling", + DisplayUnits = "count" }; _numberOfPooledConnections = _numberOfPooledConnections ?? new PollingCounter("number-of-pooled-connections", this, () => _pooledConnectionsCounter) { - DisplayName = "Number of connections are managed by connection pooler", - DisplayUnits = "count / sec" + DisplayName = "Number of connections managed by the connection pool", + DisplayUnits = "count" }; _numberOfActiveConnectionPoolGroups = _numberOfActiveConnectionPoolGroups ?? @@ -162,7 +162,7 @@ protected override void EventCommandMethodCall(EventCommandEventArgs command) _numberOfFreeConnections = _numberOfFreeConnections ?? new PollingCounter("number-of-free-connections", this, () => _freeConnectionsCounter) { - DisplayName = "Number of free-ready connections", + DisplayName = "Number of ready connections in the connection pool", DisplayUnits = "count" }; @@ -241,7 +241,7 @@ internal override void ExitNonPooledConnection() } /// - /// The number of connections that are managed by the connection pooler + /// The number of connections that are managed by the connection pool /// [NonEvent] internal override void EnterPooledConnection() @@ -250,7 +250,7 @@ internal override void EnterPooledConnection() } /// - /// The number of connections that are managed by the connection pooler + /// The number of connections that are managed by the connection pool /// [NonEvent] internal override void ExitPooledConnection() From cc9f3b5df7878477b02a655d334ec6e46d4da242 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Wed, 2 Jun 2021 13:01:08 -0700 Subject: [PATCH 54/87] Add multiuser key store provider samples (#1093) --- ...tProvider_ColumnEncryptionKeyCacheScope.cs | 20 ++++++++++++ ...ustomKeyStoreProvider_CommandPrecedence.cs | 32 +++++++++++++++++++ ...omKeyStoreProvider_ConnectionPrecedence.cs | 23 +++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs create mode 100644 doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs create mode 100644 doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs diff --git a/doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs b/doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs new file mode 100644 index 0000000000..c91e3edd7c --- /dev/null +++ b/doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs @@ -0,0 +1,20 @@ +// +class Program +{ + static void Main() + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + using (SqlCommand command = connection.CreateCommand()) + { + Dictionary customKeyStoreProviders = new Dictionary(); + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customKeyStoreProviders); + // Perform database operation using Azure Key Vault Provider + // Any decrypted column encryption keys will be cached + } // Column encryption key cache of "azureKeyVaultProvider" is cleared when "azureKeyVaultProvider" goes out of scope + } + } +} +// diff --git a/doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs b/doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs new file mode 100644 index 0000000000..d70410df59 --- /dev/null +++ b/doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs @@ -0,0 +1,32 @@ +// +class Program +{ + static void Main() + { + Dictionary customKeyStoreProviders = new Dictionary(); + MyCustomKeyStoreProvider firstProvider = new MyCustomKeyStoreProvider(); + customKeyStoreProviders.Add("FIRST_CUSTOM_STORE", firstProvider); + // Registers the provider globally + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders); + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + customKeyStoreProviders.Clear(); + MyCustomKeyStoreProvider secondProvider = new MyCustomKeyStoreProvider(); + customKeyStoreProviders.Add("SECOND_CUSTOM_STORE", secondProvider); + // Registers the provider on the connection + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders); + + using (SqlCommand command = connection.CreateCommand()) + { + customKeyStoreProviders.Clear(); + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + // Registers the provider on the command + // These providers will take precedence over connection-level providers and globally registered providers + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customKeyStoreProviders); + } + } + } +} +// diff --git a/doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs b/doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs new file mode 100644 index 0000000000..e51ec09c83 --- /dev/null +++ b/doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs @@ -0,0 +1,23 @@ +// +class Program +{ + static void Main() + { + Dictionary customKeyStoreProviders = new Dictionary(); + MyCustomKeyStoreProvider myProvider = new MyCustomKeyStoreProvider(); + customKeyStoreProviders.Add("MY_CUSTOM_STORE", myProvider); + // Registers the provider globally + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders); + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + customKeyStoreProviders.Clear(); + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + // Registers the provider on the connection + // These providers will take precedence over globally registered providers + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders); + } + } +} +// From 21475c8ed5d935e4a010d0a3d83cfd265cb7b01d Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 2 Jun 2021 15:12:07 -0700 Subject: [PATCH 55/87] Tests | Add Local DB App Name config property (#1088) --- BUILDGUIDE.md | 2 +- .../tests/ManualTests/DataCommon/DataTestUtility.cs | 6 +++--- .../SQL/ExceptionTest/ConnectionExceptionTest.cs | 2 +- .../tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs | 6 +++--- .../tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs | 2 +- .../config.default.json | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index 5a7d0080e1..3cddb58e7b 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -117,7 +117,7 @@ Manual Tests require the below setup to run: |AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ | |AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ | |AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ | - |SupportsLocalDb | (Optional) Whether or not a LocalDb instance of SQL Server is installed on the machine running the tests. |`true` OR `false`| + |LocalDbAppName | (Optional) If Local Db Testing is supported, this property configures the name of Local DB App instance available in client environment. Empty string value disables Local Db testing. | Name of Local Db App to connect to.| |SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`| |SupportsFileStream | (Optional) Whether or not FileStream is enabled on SQL Server| `true` OR `false`| |UseManagedSNIOnWindows | (Optional) Enables testing with Managed SNI on Windows| `true` OR `false`| diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 2aefb3fe36..5d5803b96a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -38,12 +38,12 @@ public static class DataTestUtility public static readonly string AKVTenantId = null; public static readonly string AKVClientId = null; public static readonly string AKVClientSecret = null; + public static readonly string LocalDbAppName = null; public static List AEConnStrings = new List(); public static List AEConnStringsSetup = new List(); public static readonly bool EnclaveEnabled = false; public static readonly bool TracingEnabled = false; public static readonly bool SupportsIntegratedSecurity = false; - public static readonly bool SupportsLocalDb = false; public static readonly bool SupportsFileStream = false; public static readonly bool UseManagedSNIOnWindows = false; public static readonly bool IsAzureSynapse = false; @@ -81,7 +81,7 @@ static DataTestUtility() AADPasswordConnectionString = c.AADPasswordConnectionString; AADServicePrincipalId = c.AADServicePrincipalId; AADServicePrincipalSecret = c.AADServicePrincipalSecret; - SupportsLocalDb = c.SupportsLocalDb; + LocalDbAppName = c.LocalDbAppName; SupportsIntegratedSecurity = c.SupportsIntegratedSecurity; SupportsFileStream = c.SupportsFileStream; EnclaveEnabled = c.EnclaveEnabled; @@ -441,7 +441,7 @@ public static void DropDatabase(SqlConnection sqlConnection, string dbName) } } - public static bool IsLocalDBInstalled() => SupportsLocalDb; + public static bool IsLocalDBInstalled() => !string.IsNullOrEmpty(LocalDbAppName?.Trim()); public static bool IsIntegratedSecuritySetup() => SupportsIntegratedSecurity; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ConnectionExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ConnectionExceptionTest.cs index 726aeafe47..97243d4abd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ConnectionExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ConnectionExceptionTest.cs @@ -147,7 +147,7 @@ public void NamedPipeInvalidConnStringTest() [SkipOnTargetFramework(~TargetFrameworkMonikers.Uap)] public static void LocalDBNotSupportedOnUapTest() { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(@"server=(localdb)\MSSQLLocalDB") + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(@$"server=(localdb)\{DataTestUtility.LocalDbAppName}") { IntegratedSecurity = true, ConnectTimeout = 2 diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs index b7e403d472..06defb1125 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs @@ -14,7 +14,7 @@ public static class LocalDBTest [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void LocalDBConnectionTest() { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(@"server=(localdb)\MSSQLLocalDB"); + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(@$"server=(localdb)\{DataTestUtility.LocalDbAppName}"); builder.IntegratedSecurity = true; builder.ConnectTimeout = 2; OpenConnection(builder.ConnectionString); @@ -24,7 +24,7 @@ public static void LocalDBConnectionTest() [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void LocalDBMarsTest() { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(@"server=(localdb)\MSSQLLocalDB;"); + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(@$"server=(localdb)\{DataTestUtility.LocalDbAppName}"); builder.IntegratedSecurity = true; builder.MultipleActiveResultSets = true; builder.ConnectTimeout = 2; @@ -35,7 +35,7 @@ public static void LocalDBMarsTest() [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void InvalidDBTest() { - using (var connection = new SqlConnection(@"Data Source=(localdb)\MSSQLLOCALDB;Database=DOES_NOT_EXIST;Pooling=false;")) + using (var connection = new SqlConnection(@$"server=(localdb)\{DataTestUtility.LocalDbAppName};Database=DOES_NOT_EXIST;Pooling=false;")) { DataTestUtility.AssertThrowsWrapper(() => connection.Open()); } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs index 8379b81baa..c5a3da8c3e 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs @@ -23,10 +23,10 @@ public class Config public string AzureKeyVaultTenantId = null; public string AzureKeyVaultClientId = null; public string AzureKeyVaultClientSecret = null; + public string LocalDbAppName = null; public bool EnclaveEnabled = false; public bool TracingEnabled = false; public bool SupportsIntegratedSecurity = false; - public bool SupportsLocalDb = false; public bool SupportsFileStream = false; public bool UseManagedSNIOnWindows = false; public string DNSCachingConnString = null; diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json index 6c631187b5..8d50730a33 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json @@ -15,7 +15,7 @@ "AzureKeyVaultClientId": "", "AzureKeyVaultClientSecret": "", "SupportsIntegratedSecurity": true, - "SupportsLocalDb": false, + "LocalDbAppName": "", "SupportsFileStream": false, "UseManagedSNIOnWindows": false, "DNSCachingConnString": "", From fbf8e0cebb96488b02a2b0a5b436d21f63cb2a36 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 3 Jun 2021 09:59:23 -0700 Subject: [PATCH 56/87] Fix doc issues for SqlDependency (#1096) Tested on docs build. --- doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml index 4e00d94732..379fb5c930 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml @@ -206,7 +206,7 @@ - Stops a listener for a connection specified in a previous call. + Stops a listener for a connection specified in a previous call. Connection string for the instance of SQL Server that was used in a previous call. - Stops a listener for a connection specified in a previous call. + Stops a listener for a connection specified in a previous call. if the listener was completely stopped; if the was unbound from the listener, but there are is at least one other using the same listener. @@ -237,7 +237,7 @@ Connection string for the instance of SQL Server that was used in a previous call. The SQL Server Service Broker queue that was used in a previous call. - Stops a listener for a connection specified in a previous call. + Stops a listener for a connection specified in a previous call. if the listener was completely stopped; if the was unbound from the listener, but there is at least one other using the same listener. From d3f91308d80b7c39797af45dcc8efd7d9c19e31b Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Thu, 3 Jun 2021 15:16:19 -0700 Subject: [PATCH 57/87] Update cross reference errors in key store provider registration docs (#1092) --- .../SqlColumnEncryptionKeyStoreProvider.xml | 8 ++++---- doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml | 4 ++-- doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml index 435fbc6c15..7c2dc715fd 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml @@ -2,9 +2,9 @@ Base class for all key store providers. A custom provider must derive from this class and override its member functions and then register it using - , - or - . + , + or + . For details see, Always Encrypted. @@ -66,7 +66,7 @@ The . Any caching implementation should reference the value of this property before caching a column encryption key and not cache it if the value is zero. This will avoid duplicate caching and possible user confusion when they are trying to configure key caching. + Caching implemented by custom key store providers will be disabled by the driver if the key store provider instance is registered using . Any caching implementation should reference the value of this property before caching a column encryption key and not cache it if the value is zero. This will avoid duplicate caching and possible user confusion when they are trying to configure key caching. ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index 1bc3c94f48..163723e9cd 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -2771,8 +2771,8 @@ You must set the value for this property before the command is executed for it t Dictionary of custom column encryption key providers - Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the or - methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the or + methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. A null dictionary was provided. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 81b4e407fe..0d57ac5a9a 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -1059,7 +1059,7 @@ GO Dictionary of custom column encryption key providers - Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the static methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the static methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. A null dictionary was provided. From 688b931870304114a1bb362280fe62ebb8e97f04 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Thu, 3 Jun 2021 15:16:30 -0700 Subject: [PATCH 58/87] Update cross reference errors in configurable retry logic documents (#1095) --- .../Microsoft.Data.SqlClient/SqlCommand.xml | 15 ++++++++------- .../SqlConfigurableRetryFactory.xml | 6 +++--- .../Microsoft.Data.SqlClient/SqlConnection.xml | 10 +++++----- .../SqlRetryIntervalBaseEnumerator.xml | 2 +- .../SqlRetryLogicBase.xml | 2 +- .../SqlRetryLogicBaseProvider.xml | 12 +++--------- .../SqlRetryLogicOption.xml | 2 +- 7 files changed, 22 insertions(+), 27 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index 163723e9cd..4cd8b3529b 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -2818,12 +2818,12 @@ The built-in column master key store providers that are available for the Window You must set the value for this property before the command is executed for it to take effect. To apply the retry logic, do the following steps before executing the command: -1. Define the configuration parameters by using or . +1. Define the configuration parameters by using type. 2. Create a by using one of the following static methods of the class: - - - - - - - - + - + - + - + - 3. Assign the object to the `RetryLogicProvider` property. > [!NOTE] @@ -2851,8 +2851,9 @@ The blocking connection simulates a situation like a command still running in th ### How to use with legacy asynchronous commands Besides assigning the provider to the command and executing the command, it's possible to run it directly using the following methods: -- -- +- +- +- [!code-csharp[SqlConfigurableRetryLogic_SqlCommand#4](~/../sqlclient/doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs#4)] diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml index 69420194e8..cbeb9de0b1 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml @@ -37,7 +37,7 @@ The following table shows the inner transient error list. - An object of containing the configuration for the object. + An object of containing the configuration for the object. Provides an exponential time interval retry logic provider. A object. @@ -58,7 +58,7 @@ The following table shows the inner transient error list. - An object of containing the configuration for the object. + An object of containing the configuration for the object. Provides an incremental time interval retry logic provider. A object. @@ -79,7 +79,7 @@ The following table shows the inner transient error list. - An object of containing the configuration for the object. + An object of containing the configuration for the object. Provides a fixed interval time retry logic provider. A object. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 0d57ac5a9a..db0f670ea4 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -1104,12 +1104,12 @@ The built-in column master key store providers that are available for the Window You must set the value for this property before opening the connection to take effect. To apply the retry logic, do the following steps before opening the connection: -1. Define the configuration parameters by using or . +1. Define the configuration parameters by using type. 2. Create a by using one of the following static methods of the class: - - - - - - - - + - + - + - + - 3. Assign the object to the `RetryLogicProvider` property. > [!NOTE] diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryIntervalBaseEnumerator.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryIntervalBaseEnumerator.xml index cd079b95bd..8856f2fe22 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryIntervalBaseEnumerator.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryIntervalBaseEnumerator.xml @@ -12,7 +12,7 @@ The maximum time allowed as a gap time. The minimum time allowed as a gap time. Initializes a new instance of the class. - The supplied arguments failed validation. + The supplied arguments failed validation. The default gap time of each interval. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBase.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBase.xml index 0a02389a76..3766fa205e 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBase.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBase.xml @@ -22,7 +22,7 @@ Delegate to a transient condition predicate. The function that this delegate points to must return a true value when an expected transient exception happens. - value that delegates to a function that receives a input parameter. + value that delegates to a function that receives a input parameter. The sender object. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBaseProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBaseProvider.xml index 59085c3e6d..9828d34437 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBaseProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBaseProvider.xml @@ -34,9 +34,7 @@ The object that the `function` returns when executed. The source of the event. The operation to re-execute if a transient condition occurs. - Executes a function and applies retry logic, if enabled. - - Exceptions will be reported via an aggregate exception if the execution isn't successful via retry attempts. + Executes a function and applies retry logic, if enabled. **Note:** Exceptions will be reported via an aggregate exception if the execution isn't successful via retry attempts. The return value of the `function` if it runs without exception. @@ -56,9 +54,7 @@ The source of the event. The operation to re-execute if a transient condition occurs. The cancellation instruction. - Executes a function and applies retry logic, if enabled. The cancellation token can be used to request that the operation be abandoned before the execution attempts are exceeded. - - Exceptions will be reported via the returned Task object, which will contain an aggregate exception if execution fails for all retry attempts. + Executes a function and applies retry logic, if enabled. The cancellation token can be used to request that the operation be abandoned before the execution attempts are exceeded. **Note:** Exceptions will be reported via the returned Task object, which will contain an aggregate exception if execution fails for all retry attempts. A task representing the asynchronous operation. The results of the task will be the return value of the `function`, if it runs without exception. The source of the event. The operation to re-execute if a transient condition occurs. The cancellation instruction. - Executes a function and applies retry logic, if enabled. The cancellation token can be used to request that the operation be abandoned before the execution attempts are exceeded. - - Exceptions will be reported via the returned Task object, which will contain an aggregate exception if execution fails for all retry attempts. + Executes a function and applies retry logic, if enabled. The cancellation token can be used to request that the operation be abandoned before the execution attempts are exceeded. **Note:** Exceptions will be reported via the returned Task object, which will contain an aggregate exception if execution fails for all retry attempts. A Task or an exception. Sets a pre-retry validation function on the to only include specific SQL statements. - The pre-retry validation delegate function; if the `CommandText` is authorized to retry the operation. + The pre-retry validation delegate function; if the `CommandText` is authorized to retry the operation. From 89e15c4b2132858f073f98bd01e96fb6e796aa16 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Thu, 3 Jun 2021 16:08:56 -0700 Subject: [PATCH 59/87] Extend SqlQueryMetadataCache to include enclave-required keys (#1062) --- .../Microsoft/Data/SqlClient/SqlCommand.cs | 34 ++++---- .../SqlClient/SqlInternalConnectionTds.cs | 1 + .../Data/SqlClient/SqlQueryMetadataCache.cs | 80 +++++++++++++----- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 2 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 5 ++ .../Microsoft/Data/SqlClient/SqlCommand.cs | 33 ++++---- .../SqlClient/SqlInternalConnectionTds.cs | 1 + .../Data/SqlClient/SqlQueryMetadataCache.cs | 84 ++++++++++++++----- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 2 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 5 ++ .../Data/SqlClient/EnclaveDelegate.Crypto.cs | 3 +- .../SqlClient/EnclaveDelegate.NotSupported.cs | 3 +- .../Data/SqlClient/EnclaveDelegate.cs | 3 +- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 59 +++++++++++++ .../ManualTests/DataCommon/DataTestUtility.cs | 2 +- 15 files changed, 235 insertions(+), 82 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index e520bf7e6e..e4b71d6eb2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; @@ -149,8 +150,17 @@ private enum EXECTYPE // cached metadata private _SqlMetaDataSet _cachedMetaData; - private Dictionary keysToBeSentToEnclave; - private bool requiresEnclaveComputations = false; + internal ConcurrentDictionary keysToBeSentToEnclave; + internal bool requiresEnclaveComputations = false; + + private bool ShouldCacheEncryptionMetadata + { + get + { + return !requiresEnclaveComputations || _activeConnection.Parser.AreEnclaveRetriesSupported; + } + } + internal EnclavePackage enclavePackage = null; private SqlEnclaveAttestationParameters enclaveAttestationParameters = null; private byte[] customData = null; @@ -3435,10 +3445,7 @@ private void ResetEncryptionState() } } - if (keysToBeSentToEnclave != null) - { - keysToBeSentToEnclave.Clear(); - } + keysToBeSentToEnclave?.Clear(); enclavePackage = null; requiresEnclaveComputations = false; enclaveAttestationParameters = null; @@ -4143,7 +4150,6 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi enclaveMetadataExists = false; } - if (isRequestedByEnclave) { if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl)) @@ -4173,12 +4179,12 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi if (keysToBeSentToEnclave == null) { - keysToBeSentToEnclave = new Dictionary(); - keysToBeSentToEnclave.Add(currentOrdinal, cipherInfo); + keysToBeSentToEnclave = new ConcurrentDictionary(); + keysToBeSentToEnclave.TryAdd(currentOrdinal, cipherInfo); } else if (!keysToBeSentToEnclave.ContainsKey(currentOrdinal)) { - keysToBeSentToEnclave.Add(currentOrdinal, cipherInfo); + keysToBeSentToEnclave.TryAdd(currentOrdinal, cipherInfo); } requiresEnclaveComputations = true; @@ -4315,7 +4321,6 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi while (ds.Read()) { - if (attestationInfoRead) { throw SQL.MultipleRowsReturnedForAttestationInfo(); @@ -4357,8 +4362,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi } // If we are not in Batch RPC mode, update the query cache with the encryption MD. - // Enclave based Always Encrypted implementation on server side does not support cache at this point. So we should not cache if the query requires keys to be sent to enclave - if (!BatchRPCMode && !requiresEnclaveComputations && (this._parameters != null && this._parameters.Count > 0)) + if (!BatchRPCMode && ShouldCacheEncryptionMetadata && (_parameters is not null && _parameters.Count > 0)) { SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: true); } @@ -5285,8 +5289,8 @@ internal void OnReturnStatus(int status) // If we are not in Batch RPC mode, update the query cache with the encryption MD. // We can do this now that we have distinguished between ReturnValue and ReturnStatus. // Read comment in AddQueryMetadata() for more details. - // Enclave based Always Encrypted implementation on server side does not support cache at this point. So we should not cache if the query requires keys to be sent to enclave - if (!BatchRPCMode && CachingQueryMetadataPostponed && !requiresEnclaveComputations && (this._parameters != null && this._parameters.Count > 0)) + if (!BatchRPCMode && CachingQueryMetadataPostponed && + ShouldCacheEncryptionMetadata && (_parameters is not null && _parameters.Count > 0)) { SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: false); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 8f1ba312af..d506f9ba11 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2632,6 +2632,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) Debug.Assert(_tceVersionSupported <= TdsEnums.MAX_SUPPORTED_TCE_VERSION, "Client support TCE version 2"); _parser.IsColumnEncryptionSupported = true; _parser.TceVersionSupported = _tceVersionSupported; + _parser.AreEnclaveRetriesSupported = _tceVersionSupported == 3; if (data.Length > 1) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs index 9ddc6e767a..94e91ef3aa 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Diagnostics; @@ -22,7 +23,7 @@ sealed internal class SqlQueryMetadataCache const int CacheTrimThreshold = 300; // Threshold above the cache size when we start trimming. private readonly MemoryCache _cache; - private static readonly SqlQueryMetadataCache _singletonInstance = new SqlQueryMetadataCache(); + private static readonly SqlQueryMetadataCache _singletonInstance = new(); private int _inTrim = 0; private long _cacheHits = 0; private long _cacheMisses = 0; @@ -53,17 +54,17 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) } // Check the cache to see if we have the MD for this query cached. - string cacheLookupKey = GetCacheLookupKeyFromSqlCommand(sqlCommand); - if (cacheLookupKey == null) + (string cacheLookupKey, string enclaveLookupKey) = GetCacheLookupKeysFromSqlCommand(sqlCommand); + if (cacheLookupKey is null) { IncrementCacheMisses(); return false; } - Dictionary ciperMetadataDictionary = _cache.Get(cacheLookupKey) as Dictionary; + Dictionary cipherMetadataDictionary = _cache.Get(cacheLookupKey) as Dictionary; // If we had a cache miss just return false. - if (ciperMetadataDictionary == null) + if (cipherMetadataDictionary is null) { IncrementCacheMisses(); return false; @@ -73,7 +74,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) foreach (SqlParameter param in sqlCommand.Parameters) { SqlCipherMetadata paramCiperMetadata; - bool found = ciperMetadataDictionary.TryGetValue(param.ParameterNameFixed, out paramCiperMetadata); + bool found = cipherMetadataDictionary.TryGetValue(param.ParameterNameFixed, out paramCiperMetadata); // If we failed to identify the encryption for a specific parameter, clear up the cipher MD of all parameters and exit. if (!found) @@ -88,7 +89,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) } // Cached cipher MD should never have an initialized algorithm since this would contain the key. - Debug.Assert(paramCiperMetadata == null || !paramCiperMetadata.IsAlgorithmInitialized()); + Debug.Assert(paramCiperMetadata is null || !paramCiperMetadata.IsAlgorithmInitialized()); // We were able to identify the cipher MD for this parameter, so set it on the param. param.CipherMetadata = paramCiperMetadata; @@ -100,7 +101,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) { SqlCipherMetadata cipherMdCopy = null; - if (param.CipherMetadata != null) + if (param.CipherMetadata is not null) { cipherMdCopy = new SqlCipherMetadata( param.CipherMetadata.EncryptionInfo, @@ -113,7 +114,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) param.CipherMetadata = cipherMdCopy; - if (cipherMdCopy != null) + if (cipherMdCopy is not null) { // Try to get the encryption key. If the key information is stale, this might fail. // In this case, just fail the cache lookup. @@ -143,6 +144,13 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) } } + ConcurrentDictionary enclaveKeys = + _cache.Get(enclaveLookupKey) as ConcurrentDictionary; + if (enclaveKeys is not null) + { + sqlCommand.keysToBeSentToEnclave = CreateCopyOfEnclaveKeys(enclaveKeys); + } + IncrementCacheHits(); return true; } @@ -178,19 +186,19 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // Construct the entry and put it in the cache. - string cacheLookupKey = GetCacheLookupKeyFromSqlCommand(sqlCommand); - if (cacheLookupKey == null) + (string cacheLookupKey, string enclaveLookupKey) = GetCacheLookupKeysFromSqlCommand(sqlCommand); + if (cacheLookupKey is null) { return; } - Dictionary ciperMetadataDictionary = new Dictionary(sqlCommand.Parameters.Count); + Dictionary cipherMetadataDictionary = new(sqlCommand.Parameters.Count); // Create a copy of the cipherMD that doesn't have the algorithm and put it in the cache. foreach (SqlParameter param in sqlCommand.Parameters) { SqlCipherMetadata cipherMdCopy = null; - if (param.CipherMetadata != null) + if (param.CipherMetadata is not null) { cipherMdCopy = new SqlCipherMetadata( param.CipherMetadata.EncryptionInfo, @@ -202,9 +210,9 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // Cached cipher MD should never have an initialized algorithm since this would contain the key. - Debug.Assert(cipherMdCopy == null || !cipherMdCopy.IsAlgorithmInitialized()); + Debug.Assert(cipherMdCopy is null || !cipherMdCopy.IsAlgorithmInitialized()); - ciperMetadataDictionary.Add(param.ParameterNameFixed, cipherMdCopy); + cipherMetadataDictionary.Add(param.ParameterNameFixed, cipherMdCopy); } // If the size of the cache exceeds the threshold, set that we are in trimming and trim the cache accordingly. @@ -228,7 +236,12 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // By default evict after 10 hours. - _cache.Set(cacheLookupKey, ciperMetadataDictionary, DateTimeOffset.UtcNow.AddHours(10)); + _cache.Set(cacheLookupKey, cipherMetadataDictionary, DateTimeOffset.UtcNow.AddHours(10)); + if (sqlCommand.requiresEnclaveComputations) + { + ConcurrentDictionary keysToBeCached = CreateCopyOfEnclaveKeys(sqlCommand.keysToBeSentToEnclave); + _cache.Set(enclaveLookupKey, keysToBeCached, DateTimeOffset.UtcNow.AddHours(10)); + } } /// @@ -236,13 +249,14 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu /// internal void InvalidateCacheEntry(SqlCommand sqlCommand) { - string cacheLookupKey = GetCacheLookupKeyFromSqlCommand(sqlCommand); - if (cacheLookupKey == null) + (string cacheLookupKey, string enclaveLookupKey) = GetCacheLookupKeysFromSqlCommand(sqlCommand); + if (cacheLookupKey is null) { return; } _cache.Remove(cacheLookupKey); + _cache.Remove(enclaveLookupKey); } @@ -271,26 +285,46 @@ private void ResetCacheCounts() _cacheMisses = 0; } - private String GetCacheLookupKeyFromSqlCommand(SqlCommand sqlCommand) + private (string, string) GetCacheLookupKeysFromSqlCommand(SqlCommand sqlCommand) { const int SqlIdentifierLength = 128; SqlConnection connection = sqlCommand.Connection; // Return null if we have no connection. - if (connection == null) + if (connection is null) { - return null; + return (null, null); } - StringBuilder cacheLookupKeyBuilder = new StringBuilder(connection.DataSource, capacity: connection.DataSource.Length + SqlIdentifierLength + sqlCommand.CommandText.Length + 6); + StringBuilder cacheLookupKeyBuilder = new(connection.DataSource, capacity: connection.DataSource.Length + SqlIdentifierLength + sqlCommand.CommandText.Length + 6); cacheLookupKeyBuilder.Append(":::"); // Pad database name to 128 characters to avoid any false cache matches because of weird DB names. cacheLookupKeyBuilder.Append(connection.Database.PadRight(SqlIdentifierLength)); cacheLookupKeyBuilder.Append(":::"); cacheLookupKeyBuilder.Append(sqlCommand.CommandText); - return cacheLookupKeyBuilder.ToString(); + string cacheLookupKey = cacheLookupKeyBuilder.ToString(); + string enclaveLookupKey = cacheLookupKeyBuilder.Append(":::enclaveKeys").ToString(); + return (cacheLookupKey, enclaveLookupKey); + } + + private ConcurrentDictionary CreateCopyOfEnclaveKeys(ConcurrentDictionary keysToBeSentToEnclave) + { + ConcurrentDictionary enclaveKeys = new(); + foreach (KeyValuePair kvp in keysToBeSentToEnclave) + { + int ordinal = kvp.Key; + SqlTceCipherInfoEntry original = kvp.Value; + SqlTceCipherInfoEntry copy = new(ordinal); + foreach (SqlEncryptionKeyInfo cekInfo in original.ColumnEncryptionKeyValues) + { + copy.Add(cekInfo.encryptedKey, cekInfo.databaseId, cekInfo.cekId, cekInfo.cekVersion, + cekInfo.cekMdVersion, cekInfo.keyPath, cekInfo.keyStoreName, cekInfo.algorithmName); + } + enclaveKeys.TryAdd(ordinal, copy); + } + return enclaveKeys; } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 44a8f02941..728e521847 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -985,7 +985,7 @@ internal static string GetSniContextEnumName(SniContext sniContext) } // TCE Related constants - internal const byte MAX_SUPPORTED_TCE_VERSION = 0x02; // max version + internal const byte MAX_SUPPORTED_TCE_VERSION = 0x03; // max version internal const byte MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT = 0x02; // min version with enclave support internal const ushort MAX_TCE_CIPHERINFO_SIZE = 2048; // max size of cipherinfo blob internal const long MAX_TCE_CIPHERTEXT_SIZE = 2147483648; // max size of encrypted blob- currently 2GB. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 5c526e9c30..04ec52be41 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -157,6 +157,11 @@ internal sealed partial class TdsParser /// internal byte TceVersionSupported { get; set; } + /// + /// Server supports retrying when the enclave CEKs sent by the client do not match what is needed for the query to run. + /// + internal bool AreEnclaveRetriesSupported { get; set; } + /// /// Type of enclave being used by the server /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 93002d88e4..bb0264666f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -24,6 +24,7 @@ using Microsoft.Data.Sql; using Microsoft.Data.SqlClient.Server; using SysTx = System.Transactions; +using System.Collections.Concurrent; namespace Microsoft.Data.SqlClient { @@ -120,9 +121,6 @@ private enum EXECTYPE // cut down on object creation and cache all these // cached metadata private _SqlMetaDataSet _cachedMetaData; - - private Dictionary keysToBeSentToEnclave; - private bool requiresEnclaveComputations = false; internal EnclavePackage enclavePackage = null; private SqlEnclaveAttestationParameters enclaveAttestationParameters = null; private byte[] customData = null; @@ -164,6 +162,15 @@ internal bool ShouldUseEnclaveBasedWorkflow get { return !string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) && IsColumnEncryptionEnabled; } } + internal ConcurrentDictionary keysToBeSentToEnclave; + internal bool requiresEnclaveComputations = false; + private bool ShouldCacheEncryptionMetadata + { + get + { + return !requiresEnclaveComputations || _activeConnection.Parser.AreEnclaveRetriesSupported; + } + } /// /// Per-command custom providers. It can be provided by the user and can be set more than once. /// @@ -3990,10 +3997,7 @@ private void ResetEncryptionState() } } - if (keysToBeSentToEnclave != null) - { - keysToBeSentToEnclave.Clear(); - } + keysToBeSentToEnclave?.Clear(); enclavePackage = null; requiresEnclaveComputations = false; enclaveAttestationParameters = null; @@ -4752,7 +4756,6 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi enclaveMetadataExists = false; } - if (isRequestedByEnclave) { if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl)) @@ -4782,12 +4785,12 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi if (keysToBeSentToEnclave == null) { - keysToBeSentToEnclave = new Dictionary(); - keysToBeSentToEnclave.Add(currentOrdinal, cipherInfo); + keysToBeSentToEnclave = new ConcurrentDictionary(); + keysToBeSentToEnclave.TryAdd(currentOrdinal, cipherInfo); } else if (!keysToBeSentToEnclave.ContainsKey(currentOrdinal)) { - keysToBeSentToEnclave.Add(currentOrdinal, cipherInfo); + keysToBeSentToEnclave.TryAdd(currentOrdinal, cipherInfo); } requiresEnclaveComputations = true; @@ -4919,7 +4922,6 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi while (ds.Read()) { - if (attestationInfoRead) { throw SQL.MultipleRowsReturnedForAttestationInfo(); @@ -4961,8 +4963,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi } // If we are not in Batch RPC mode, update the query cache with the encryption MD. - // Enclave based Always Encrypted implementation on server side does not support cache at this point. So we should not cache if the query requires keys to be sent to enclave - if (!BatchRPCMode && !requiresEnclaveComputations && (this._parameters != null && this._parameters.Count > 0)) + if (!BatchRPCMode && ShouldCacheEncryptionMetadata && (_parameters is not null && _parameters.Count > 0)) { SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: true); } @@ -6156,8 +6157,8 @@ internal void OnReturnStatus(int status) // If we are not in Batch RPC mode, update the query cache with the encryption MD. // We can do this now that we have distinguished between ReturnValue and ReturnStatus. // Read comment in AddQueryMetadata() for more details. - // Enclave based Always Encrypted implementation on server side does not support cache at this point. So we should not cache if the query requires keys to be sent to enclave - if (!BatchRPCMode && CachingQueryMetadataPostponed && !requiresEnclaveComputations && (this._parameters != null && this._parameters.Count > 0)) + if (!BatchRPCMode && CachingQueryMetadataPostponed && + ShouldCacheEncryptionMetadata && (_parameters is not null && _parameters.Count > 0)) { SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: false); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 42a6eec32c..b9b0a217e4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -3059,6 +3059,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) Debug.Assert(_tceVersionSupported <= TdsEnums.MAX_SUPPORTED_TCE_VERSION, "Client support TCE version 2"); _parser.IsColumnEncryptionSupported = true; _parser.TceVersionSupported = _tceVersionSupported; + _parser.AreEnclaveRetriesSupported = _tceVersionSupported == 3; if (data.Length > 1) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs index 3b31138cb4..dad8b877c6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Diagnostics; @@ -21,7 +22,7 @@ sealed internal class SqlQueryMetadataCache const int CacheTrimThreshold = 300; // Threshold above the cache size when we start trimming. private readonly MemoryCache _cache; - private static readonly SqlQueryMetadataCache _singletonInstance = new SqlQueryMetadataCache(); + private static readonly SqlQueryMetadataCache _singletonInstance = new(); private int _inTrim = 0; private long _cacheHits = 0; private long _cacheMisses = 0; @@ -55,17 +56,19 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) } // Check the cache to see if we have the MD for this query cached. - string cacheLookupKey = GetCacheLookupKeyFromSqlCommand(sqlCommand); - if (cacheLookupKey == null) + Tuple keys = GetCacheLookupKeysFromSqlCommand(sqlCommand); + string cacheLookupKey = keys?.Item1; + string enclaveLookupKey = keys?.Item2; + if (cacheLookupKey is null) { IncrementCacheMisses(); return false; } - Dictionary ciperMetadataDictionary = _cache.Get(cacheLookupKey) as Dictionary; + Dictionary cipherMetadataDictionary = _cache.Get(cacheLookupKey) as Dictionary; // If we had a cache miss just return false. - if (ciperMetadataDictionary == null) + if (cipherMetadataDictionary is null) { IncrementCacheMisses(); return false; @@ -75,7 +78,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) foreach (SqlParameter param in sqlCommand.Parameters) { SqlCipherMetadata paramCiperMetadata; - bool found = ciperMetadataDictionary.TryGetValue(param.ParameterNameFixed, out paramCiperMetadata); + bool found = cipherMetadataDictionary.TryGetValue(param.ParameterNameFixed, out paramCiperMetadata); // If we failed to identify the encryption for a specific parameter, clear up the cipher MD of all parameters and exit. if (!found) @@ -90,7 +93,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) } // Cached cipher MD should never have an initialized algorithm since this would contain the key. - Debug.Assert(paramCiperMetadata == null || !paramCiperMetadata.IsAlgorithmInitialized()); + Debug.Assert(paramCiperMetadata is null || !paramCiperMetadata.IsAlgorithmInitialized()); // We were able to identify the cipher MD for this parameter, so set it on the param. param.CipherMetadata = paramCiperMetadata; @@ -102,7 +105,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) { SqlCipherMetadata cipherMdCopy = null; - if (param.CipherMetadata != null) + if (param.CipherMetadata is not null) { cipherMdCopy = new SqlCipherMetadata( param.CipherMetadata.EncryptionInfo, @@ -115,7 +118,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) param.CipherMetadata = cipherMdCopy; - if (cipherMdCopy != null) + if (cipherMdCopy is not null) { // Try to get the encryption key. If the key information is stale, this might fail. // In this case, just fail the cache lookup. @@ -145,6 +148,13 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) } } + ConcurrentDictionary enclaveKeys = + _cache.Get(enclaveLookupKey) as ConcurrentDictionary; + if (enclaveKeys is not null) + { + sqlCommand.keysToBeSentToEnclave = CreateCopyOfEnclaveKeys(enclaveKeys); + } + IncrementCacheHits(); return true; } @@ -180,19 +190,21 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // Construct the entry and put it in the cache. - string cacheLookupKey = GetCacheLookupKeyFromSqlCommand(sqlCommand); - if (cacheLookupKey == null) + Tuple keys = GetCacheLookupKeysFromSqlCommand(sqlCommand); + string cacheLookupKey = keys?.Item1; + string enclaveLookupKey = keys?.Item2; + if (cacheLookupKey is null) { return; } - Dictionary ciperMetadataDictionary = new Dictionary(sqlCommand.Parameters.Count); + Dictionary cipherMetadataDictionary = new(sqlCommand.Parameters.Count); // Create a copy of the cipherMD that doesn't have the algorithm and put it in the cache. foreach (SqlParameter param in sqlCommand.Parameters) { SqlCipherMetadata cipherMdCopy = null; - if (param.CipherMetadata != null) + if (param.CipherMetadata is not null) { cipherMdCopy = new SqlCipherMetadata( param.CipherMetadata.EncryptionInfo, @@ -204,9 +216,9 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // Cached cipher MD should never have an initialized algorithm since this would contain the key. - Debug.Assert(cipherMdCopy == null || !cipherMdCopy.IsAlgorithmInitialized()); + Debug.Assert(cipherMdCopy is null || !cipherMdCopy.IsAlgorithmInitialized()); - ciperMetadataDictionary.Add(param.ParameterNameFixed, cipherMdCopy); + cipherMetadataDictionary.Add(param.ParameterNameFixed, cipherMdCopy); } // If the size of the cache exceeds the threshold, set that we are in trimming and trim the cache accordingly. @@ -230,7 +242,12 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // By default evict after 10 hours. - _cache.Set(cacheLookupKey, ciperMetadataDictionary, DateTimeOffset.UtcNow.AddHours(10)); + _cache.Set(cacheLookupKey, cipherMetadataDictionary, DateTimeOffset.UtcNow.AddHours(10)); + if (sqlCommand.requiresEnclaveComputations) + { + ConcurrentDictionary keysToBeCached = CreateCopyOfEnclaveKeys(sqlCommand.keysToBeSentToEnclave); + _cache.Set(enclaveLookupKey, keysToBeCached, DateTimeOffset.UtcNow.AddHours(10)); + } } /// @@ -238,13 +255,16 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu /// internal void InvalidateCacheEntry(SqlCommand sqlCommand) { - string cacheLookupKey = GetCacheLookupKeyFromSqlCommand(sqlCommand); - if (cacheLookupKey == null) + Tuple keys = GetCacheLookupKeysFromSqlCommand(sqlCommand); + string cacheLookupKey = keys?.Item1; + string enclaveLookupKey = keys?.Item2; + if (cacheLookupKey is null) { return; } _cache.Remove(cacheLookupKey); + _cache.Remove(enclaveLookupKey); } @@ -273,26 +293,46 @@ private void ResetCacheCounts() _cacheMisses = 0; } - private String GetCacheLookupKeyFromSqlCommand(SqlCommand sqlCommand) + private Tuple GetCacheLookupKeysFromSqlCommand(SqlCommand sqlCommand) { const int SqlIdentifierLength = 128; SqlConnection connection = sqlCommand.Connection; // Return null if we have no connection. - if (connection == null) + if (connection is null) { return null; } - StringBuilder cacheLookupKeyBuilder = new StringBuilder(connection.DataSource, capacity: connection.DataSource.Length + SqlIdentifierLength + sqlCommand.CommandText.Length + 6); + StringBuilder cacheLookupKeyBuilder = new(connection.DataSource, capacity: connection.DataSource.Length + SqlIdentifierLength + sqlCommand.CommandText.Length + 6); cacheLookupKeyBuilder.Append(":::"); // Pad database name to 128 characters to avoid any false cache matches because of weird DB names. cacheLookupKeyBuilder.Append(connection.Database.PadRight(SqlIdentifierLength)); cacheLookupKeyBuilder.Append(":::"); cacheLookupKeyBuilder.Append(sqlCommand.CommandText); - return cacheLookupKeyBuilder.ToString(); + string cacheLookupKey = cacheLookupKeyBuilder.ToString(); + string enclaveLookupKey = cacheLookupKeyBuilder.Append(":::enclaveKeys").ToString(); + return Tuple.Create(cacheLookupKey, enclaveLookupKey); + } + + private ConcurrentDictionary CreateCopyOfEnclaveKeys(ConcurrentDictionary keysToBeSentToEnclave) + { + ConcurrentDictionary enclaveKeys = new(); + foreach (KeyValuePair kvp in keysToBeSentToEnclave) + { + int ordinal = kvp.Key; + SqlTceCipherInfoEntry original = kvp.Value; + SqlTceCipherInfoEntry copy = new(ordinal); + foreach (SqlEncryptionKeyInfo cekInfo in original.ColumnEncryptionKeyValues) + { + copy.Add(cekInfo.encryptedKey, cekInfo.databaseId, cekInfo.cekId, cekInfo.cekVersion, + cekInfo.cekMdVersion, cekInfo.keyPath, cekInfo.keyStoreName, cekInfo.algorithmName); + } + enclaveKeys.TryAdd(ordinal, copy); + } + return enclaveKeys; } } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 417657eb49..20a7fad79e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -949,7 +949,7 @@ internal enum FedAuthInfoId : byte internal const byte DATA_CLASSIFICATION_VERSION_MAX_SUPPORTED = 0x02; // TCE Related constants - internal const byte MAX_SUPPORTED_TCE_VERSION = 0x02; // max version + internal const byte MAX_SUPPORTED_TCE_VERSION = 0x03; // max version internal const byte MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT = 0x02; // min version with enclave support internal const ushort MAX_TCE_CIPHERINFO_SIZE = 2048; // max size of cipherinfo blob internal const long MAX_TCE_CIPHERTEXT_SIZE = 2147483648; // max size of encrypted blob- currently 2GB. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 328867ddca..f1a714f319 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -279,6 +279,11 @@ internal bool IsColumnEncryptionSupported /// internal byte TceVersionSupported { get; set; } + /// + /// Server supports retrying when the enclave CEKs sent by the client do not match what is needed for the query to run. + /// + internal bool AreEnclaveRetriesSupported { get; set; } + /// /// Type of enclave being used by the server /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs index b679414cbe..241c6aaf58 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; namespace Microsoft.Data.SqlClient @@ -132,7 +133,7 @@ private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttes /// connection executing the query /// command executing the query /// - internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysToBeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection, SqlCommand command) + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, ConcurrentDictionary keysToBeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection, SqlCommand command) { SqlEnclaveSession sqlEnclaveSession; long counter; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs index 19d1d1f58f..7b43c8cb66 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.NotSupported.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; namespace Microsoft.Data.SqlClient @@ -36,7 +37,7 @@ internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProt throw new PlatformNotSupportedException(); } - internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection, SqlCommand command) + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, ConcurrentDictionary keysTobeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlConnection connection, SqlCommand command) { throw new PlatformNotSupportedException(); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index dc45b979f3..fc10fda351 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; @@ -48,7 +49,7 @@ private byte[] GetUintBytes(string enclaveType, int intValue, string variableNam /// /// /// - private List GetDecryptedKeysToBeSentToEnclave(Dictionary keysTobeSentToEnclave, string serverName, SqlConnection connection, SqlCommand command) + private List GetDecryptedKeysToBeSentToEnclave(ConcurrentDictionary keysTobeSentToEnclave, string serverName, SqlConnection connection, SqlCommand command) { List decryptedKeysToBeSentToEnclave = new List(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 8f18312f94..31264fa396 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -2242,6 +2242,65 @@ public void TestCommandCustomKeyStoreProviderDuringAeQuery(string connectionStri } } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.EnclaveEnabled))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestRetryWhenAEParameterMetadataCacheIsStale(string connectionString) + { + CleanUpTable(connectionString, _tableName); + + const int customerId = 50; + IList values = GetValues(dataHint: customerId); + InsertRows(tableName: _tableName, numberofRows: 1, values: values, connection: connectionString); + + ApiTestTable table = _fixture.ApiTestTable as ApiTestTable; + string enclaveSelectQuery = $@"SELECT CustomerId, FirstName, LastName FROM [{_tableName}] WHERE CustomerId > @CustomerId"; + string alterCekQueryFormatString = "ALTER TABLE [{0}] " + + "ALTER COLUMN [CustomerId] [int] " + + "ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{1}], " + + "ENCRYPTION_TYPE = Randomized, " + + "ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'); " + + "ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;"; + + using SqlConnection sqlConnection = new(connectionString); + sqlConnection.Open(); + + // execute the select query to add its parameter metadata and enclave-required CEKs to the cache + using SqlCommand cmd = new SqlCommand(enclaveSelectQuery, sqlConnection, null, SqlCommandColumnEncryptionSetting.Enabled); + cmd.Parameters.AddWithValue("CustomerId", 0); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + Assert.Equal(customerId, (int)reader[0]); + } + reader.Close(); + }; + + // change the CEK for the CustomerId column from ColumnEncryptionKey1 to ColumnEncryptionKey2 + // this will render the select query's cache entry stale + cmd.Parameters.Clear(); + cmd.CommandText = string.Format(alterCekQueryFormatString, _tableName, table.columnEncryptionKey2.Name); + cmd.ExecuteNonQuery(); + + // execute the select query again. it will attempt to use the stale cache entry, receive + // a retryable error from the server, remove the stale cache entry, retry and succeed + cmd.CommandText = enclaveSelectQuery; + cmd.Parameters.AddWithValue("@CustomerId", 0); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + Assert.Equal(customerId, (int)reader[0]); + } + reader.Close(); + } + + // revert the CEK change to the CustomerId column + cmd.Parameters.Clear(); + cmd.CommandText = string.Format(alterCekQueryFormatString, _tableName, table.columnEncryptionKey1.Name); + cmd.ExecuteNonQuery(); + } + private void ExecuteQueryThatRequiresCustomKeyStoreProvider(SqlConnection connection) { using (SqlCommand command = CreateCommandThatRequiresCustomKeyStoreProvider(connection)) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 5d5803b96a..1f6ad4ddea 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -41,7 +41,7 @@ public static class DataTestUtility public static readonly string LocalDbAppName = null; public static List AEConnStrings = new List(); public static List AEConnStringsSetup = new List(); - public static readonly bool EnclaveEnabled = false; + public static bool EnclaveEnabled { get; private set; } = false; public static readonly bool TracingEnabled = false; public static readonly bool SupportsIntegratedSecurity = false; public static readonly bool SupportsFileStream = false; From 6ba4694bb6e4cdd7d7c0a6554df0de295ed2f954 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 3 Jun 2021 17:31:48 -0700 Subject: [PATCH 60/87] Tests | Fix issues with Azure Synapse (#1099) --- .../SQL/AsyncTest/AsyncTimeoutTest.cs | 3 +- .../SQL/DataReaderTest/DataReaderTest.cs | 7 +- .../SqlRandomTypeInfoCollection.cs | 71 ++++++-- .../SqlRandomTypesForSqlServer.cs | 46 +++++- .../RetryLogic/SqlCommandReliabilityTest.cs | 151 ++++++++++-------- .../SqlConfigurationManagerReliabilityTest.cs | 25 ++- .../SqlConnectionReliabilityTest.cs | 2 +- .../SQL/TransactionTest/TransactionTest.cs | 19 ++- 8 files changed, 225 insertions(+), 99 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs index 0ba98d83b6..b516fa1a11 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs @@ -25,7 +25,8 @@ public enum AsyncAPI ExecuteXmlReaderAsync } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // Synapse: WAITFOR DELAY not supported [Parse error at line: 1, column: 1: Incorrect syntax near 'WAITFOR'.] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] [ClassData(typeof(AsyncTimeoutTestVariations))] public static void TestDelayedAsyncTimeout(AsyncAPI api, string commonObj, int delayPeriod, bool marsEnabled) => RunTest(api, commonObj, delayPeriod, marsEnabled); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs index 1d1ff574e5..448aa9ed64 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -136,7 +136,7 @@ public static void CheckSparseColumnBit() } // Synapse: Statement 'Drop Database' is not supported in this version of SQL Server. - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse))] public static void CollatedDataReaderTest() { var databaseName = DataTestUtility.GetUniqueName("DB"); @@ -293,7 +293,7 @@ public static void CheckHiddenColumns() Assert.Contains("user_id", names, StringComparer.Ordinal); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void CheckNullRowVersionIsBDNull() { lock (s_rowVersionLock) @@ -323,7 +323,8 @@ public static void CheckNullRowVersionIsBDNull() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // Synapse: Cannot find data type 'rowversion'. + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void CheckLegacyNullRowVersionIsEmptyArray() { lock (s_rowVersionLock) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/SqlRandomTypeInfoCollection.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/SqlRandomTypeInfoCollection.cs index 7ed20404a1..951e66931c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/SqlRandomTypeInfoCollection.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/SqlRandomTypeInfoCollection.cs @@ -10,12 +10,12 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { /// - /// Defines a collection of types to be used by the test. Tests can start with CreateSql2005Collection or + /// Defines a collection of types to be used by the test. Tests start with /// CreateSql2008Collection and add/remove types, as needed. /// public sealed class SqlRandomTypeInfoCollection : System.Collections.ObjectModel.KeyedCollection { - private static readonly SqlRandomTypeInfo[] s_sql2005Types = + private static readonly SqlRandomTypeInfo[] s_sql2008Types = { // var types new SqlVarBinaryTypeInfo(), @@ -67,11 +67,62 @@ public sealed class SqlRandomTypeInfoCollection : System.Collections.ObjectModel // xml new SqlXmlTypeInfo(), + + // date/time types + new SqlDateTypeInfo(), + new SqlDateTime2TypeInfo(), + new SqlDateTimeOffsetTypeInfo(), + new SqlTimeTypeInfo(), }; - // types added in SQL 2008 - private static readonly SqlRandomTypeInfo[] s_newInSql2008Types = + private static readonly SqlRandomTypeInfo[] s_sqlSynapseTypes = { + // var types + new SqlVarBinaryTypeInfo(), + new SqlVarCharTypeInfo(), + new SqlNVarCharTypeInfo(), + + // integer data types + new SqlBigIntTypeInfo(), + new SqlIntTypeInfo(), + new SqlSmallIntTypeInfo(), + new SqlTinyIntTypeInfo(), + + // fixed length blobs + new SqlCharTypeInfo(), + new SqlNCharTypeInfo(), + new SqlBinaryTypeInfo(), + + // large blobs + new SqlImageTypeInfo(), + + // bit + new SqlBitTypeInfo(), + + // decimal + new SqlDecimalTypeInfo(), + + // money types + new SqlMoneyTypeInfo(), + new SqlSmallMoneyTypeInfo(), + + // float types + new SqRealTypeInfo(), + new SqFloatTypeInfo(), + + // typestamp (== rowversion) + new SqlRowVersionTypeInfo(), + + // unique identifier (== guid) + new SqlUniqueIdentifierTypeInfo(), + + // date/time types + new SqlDateTimeTypeInfo(), + new SqlSmallDateTimeTypeInfo(), + + // variant + new SqlVariantTypeInfo(), + // date/time types new SqlDateTypeInfo(), new SqlDateTime2TypeInfo(), @@ -152,19 +203,11 @@ public void AddRange(SqlRandomTypeInfo[] types) } /// - /// creates a collection of types supported in SQL 2005 - /// - public static SqlRandomTypeInfoCollection CreateSql2005Collection() - { - return new SqlRandomTypeInfoCollection(s_sql2005Types); - } - - /// - /// creates a collection of types supported in SQL 2005 and 2008 + /// creates a collection of types supported in SQL 2008+ /// public static SqlRandomTypeInfoCollection CreateSql2008Collection() { - return new SqlRandomTypeInfoCollection(s_sql2005Types, s_newInSql2008Types); + return DataTestUtility.IsNotAzureSynapse() ? new SqlRandomTypeInfoCollection(s_sql2008Types) : new SqlRandomTypeInfoCollection(s_sqlSynapseTypes); } /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/SqlRandomTypesForSqlServer.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/SqlRandomTypesForSqlServer.cs index ec8a80454e..8663509ad7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/SqlRandomTypesForSqlServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/SqlRandomTypesForSqlServer.cs @@ -1315,6 +1315,49 @@ internal sealed class SqlVariantTypeInfo : SqlRandomTypeInfo new SqlSmallDateTimeTypeInfo(), }; + private static readonly SqlRandomTypeInfo[] s_variantSubTypesSynapse = + { + // var types + new SqlVarBinaryTypeInfo(), + new SqlVarCharTypeInfo(), + new SqlNVarCharTypeInfo(), + + // integer data types + new SqlBigIntTypeInfo(), + new SqlIntTypeInfo(), + new SqlSmallIntTypeInfo(), + new SqlTinyIntTypeInfo(), + + // fixed length blobs + new SqlCharTypeInfo(), + new SqlNCharTypeInfo(), + new SqlBinaryTypeInfo(), + + // large blobs + new SqlImageTypeInfo(), + + // bit + new SqlBitTypeInfo(), + + // decimal + new SqlDecimalTypeInfo(), + + // money types + new SqlMoneyTypeInfo(), + new SqlSmallMoneyTypeInfo(), + + // float types + new SqRealTypeInfo(), + new SqFloatTypeInfo(), + + // unique identifier (== guid) + new SqlUniqueIdentifierTypeInfo(), + + // date/time types + new SqlDateTimeTypeInfo(), + new SqlSmallDateTimeTypeInfo(), + }; + public SqlVariantTypeInfo() : base(SqlDbType.Variant) { @@ -1338,7 +1381,8 @@ private static bool IsUnicodeType(SqlDbType t) protected override object CreateRandomValueInternal(SqlRandomizer rand, SqlRandomTableColumn columnInfo) { - SqlRandomTypeInfo subType = s_variantSubTypes[rand.NextIntInclusive(0, maxValueInclusive: s_variantSubTypes.Length - 1)]; + SqlRandomTypeInfo subType = DataTestUtility.IsNotAzureSynapse() ? s_variantSubTypes[rand.NextIntInclusive(0, maxValueInclusive: s_variantSubTypes.Length - 1)] : s_variantSubTypesSynapse[rand.NextIntInclusive(0, maxValueInclusive: s_variantSubTypesSynapse.Length - 1)]; + object val = subType.CreateRandomValue(rand, new SqlRandomTableColumn(subType, SqlRandomColumnOptions.None, 8000)); char[] cval = val as char[]; if (cval != null) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs index 234b60c9e6..ff46122f81 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs @@ -51,11 +51,14 @@ public void RetryExecuteFail(string cnnString, SqlRetryLogicBaseProvider provide Assert.Equal(numberOfTries, ex.InnerExceptions.Count); Assert.Contains(string.Format(_exceedErrMsgPattern, numberOfTries), ex.Message); - cmd.CommandText = query + " FOR XML AUTO"; - ex = Assert.Throws(() => cmd.ExecuteXmlReader()); - Assert.Equal(numberOfTries, currentRetries + 1); - Assert.Equal(numberOfTries, ex.InnerExceptions.Count); - Assert.Contains(string.Format(_exceedErrMsgPattern, numberOfTries), ex.Message); + if (!DataTestUtility.IsAzureSynapse) + { + cmd.CommandText = query + " FOR XML AUTO"; + ex = Assert.Throws(() => cmd.ExecuteXmlReader()); + Assert.Equal(numberOfTries, currentRetries + 1); + Assert.Equal(numberOfTries, ex.InnerExceptions.Count); + Assert.Contains(string.Format(_exceedErrMsgPattern, numberOfTries), ex.Message); + } } } @@ -93,16 +96,19 @@ public void RetryExecuteCancel(string cnnString, SqlRetryLogicBaseProvider provi Assert.Equal(cancelAfterRetries, ex.InnerExceptions.Count); Assert.Contains(string.Format(_cancelErrMsgPattern, currentRetries), ex.Message); - cmd.CommandText = query + " FOR XML AUTO"; - ex = Assert.Throws(() => cmd.ExecuteXmlReader()); - Assert.Equal(cancelAfterRetries, currentRetries); - Assert.Equal(cancelAfterRetries, ex.InnerExceptions.Count); - Assert.Contains(string.Format(_cancelErrMsgPattern, currentRetries), ex.Message); + if (DataTestUtility.IsNotAzureSynapse()) + { + cmd.CommandText = query + " FOR XML AUTO"; + ex = Assert.Throws(() => cmd.ExecuteXmlReader()); + Assert.Equal(cancelAfterRetries, currentRetries); + Assert.Equal(cancelAfterRetries, ex.InnerExceptions.Count); + Assert.Contains(string.Format(_cancelErrMsgPattern, currentRetries), ex.Message); + } } } [ActiveIssue(14588)] - [Theory] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper))] public void RetryExecuteWithTransScope(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -137,7 +143,8 @@ public void RetryExecuteWithTransScope(string cnnString, SqlRetryLogicBaseProvid } } - [Theory] + // Synapse: 111214;An attempt to complete a transaction has failed. No corresponding transaction found. + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper))] public void RetryExecuteWithTrans(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -173,7 +180,8 @@ public void RetryExecuteWithTrans(string cnnString, SqlRetryLogicBaseProvider pr } } - [Theory] + // Synapse: Msg 103010, Level 16, State 1, Line 1 | Parse error at line: 1, column: 1: Incorrect syntax near 'command' + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyFilterDMLStatements), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper))] public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -227,7 +235,7 @@ public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLo [ActiveIssue(14325)] // avoid creating a new database in Azure - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyDropDB), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper))] public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -288,7 +296,8 @@ public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBase } // In Managed SNI by Named pipe connection, SqlCommand doesn't respect timeout. "ActiveIssue 12167" - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotUsingManagedSNIOnWindows))] + // Synapse: Does not support WAITFOR DELAY. + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotUsingManagedSNIOnWindows), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyLockedTable), parameters: new object[] { 10 }, MemberType = typeof(RetryLogicTestHelper))] public void UpdateALockedTable(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -368,9 +377,12 @@ public void NoneRetriableExecuteFail(string cnnString, SqlRetryLogicBaseProvider Assert.ThrowsAsync(() => cmd.ExecuteReaderAsync(CommandBehavior.Default)).Wait(); Assert.ThrowsAsync(() => cmd.ExecuteNonQueryAsync()).Wait(); - cmd.CommandText = query + " FOR XML AUTO"; - Assert.Throws(() => cmd.ExecuteXmlReader()); - Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()).Wait(); + if (DataTestUtility.IsNotAzureSynapse()) + { + cmd.CommandText = query + " FOR XML AUTO"; + Assert.Throws(() => cmd.ExecuteXmlReader()); + Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()).Wait(); + } } } #endregion @@ -425,16 +437,19 @@ public async void RetryExecuteAsyncFail(string cnnString, SqlRetryLogicBaseProvi Assert.Equal(numberOfTries, ex.InnerExceptions.Count); Assert.Contains(string.Format(_exceedErrMsgPattern, numberOfTries), ex.Message); - cmd.CommandText = query + " FOR XML AUTO"; - ex = await Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()); - Assert.Equal(numberOfTries, currentRetries + 1); - Assert.Equal(numberOfTries, ex.InnerExceptions.Count); - Assert.Contains(string.Format(_exceedErrMsgPattern, numberOfTries), ex.Message); - - ex = await Assert.ThrowsAsync(() => provider.ExecuteAsync(cmd, () => Task.Factory.FromAsync(cmd.BeginExecuteXmlReader(), cmd.EndExecuteXmlReader))); - Assert.Equal(numberOfTries, currentRetries + 1); - Assert.Equal(numberOfTries, ex.InnerExceptions.Count); - Assert.Contains(string.Format(_exceedErrMsgPattern, numberOfTries), ex.Message); + if (DataTestUtility.IsNotAzureSynapse()) + { + cmd.CommandText = query + " FOR XML AUTO"; + ex = await Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()); + Assert.Equal(numberOfTries, currentRetries + 1); + Assert.Equal(numberOfTries, ex.InnerExceptions.Count); + Assert.Contains(string.Format(_exceedErrMsgPattern, numberOfTries), ex.Message); + + ex = await Assert.ThrowsAsync(() => provider.ExecuteAsync(cmd, () => Task.Factory.FromAsync(cmd.BeginExecuteXmlReader(), cmd.EndExecuteXmlReader))); + Assert.Equal(numberOfTries, currentRetries + 1); + Assert.Equal(numberOfTries, ex.InnerExceptions.Count); + Assert.Contains(string.Format(_exceedErrMsgPattern, numberOfTries), ex.Message); + } } } @@ -487,16 +502,19 @@ public async void RetryExecuteAsyncCancel(string cnnString, SqlRetryLogicBasePro Assert.Equal(cancelAfterRetries, ex.InnerExceptions.Count); Assert.Contains(string.Format(_cancelErrMsgPattern, currentRetries), ex.Message); - cmd.CommandText = query + " FOR XML AUTO"; - ex = await Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()); - Assert.Equal(cancelAfterRetries, currentRetries); - Assert.Equal(cancelAfterRetries, ex.InnerExceptions.Count); - Assert.Contains(string.Format(_cancelErrMsgPattern, currentRetries), ex.Message); - - ex = await Assert.ThrowsAsync(() => provider.ExecuteAsync(cmd, () => Task.Factory.FromAsync(cmd.BeginExecuteXmlReader(), cmd.EndExecuteXmlReader))); - Assert.Equal(cancelAfterRetries, currentRetries); - Assert.Equal(cancelAfterRetries, ex.InnerExceptions.Count); - Assert.Contains(string.Format(_cancelErrMsgPattern, currentRetries), ex.Message); + if (DataTestUtility.IsNotAzureSynapse()) + { + cmd.CommandText = query + " FOR XML AUTO"; + ex = await Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()); + Assert.Equal(cancelAfterRetries, currentRetries); + Assert.Equal(cancelAfterRetries, ex.InnerExceptions.Count); + Assert.Contains(string.Format(_cancelErrMsgPattern, currentRetries), ex.Message); + + ex = await Assert.ThrowsAsync(() => provider.ExecuteAsync(cmd, () => Task.Factory.FromAsync(cmd.BeginExecuteXmlReader(), cmd.EndExecuteXmlReader))); + Assert.Equal(cancelAfterRetries, currentRetries); + Assert.Equal(cancelAfterRetries, ex.InnerExceptions.Count); + Assert.Contains(string.Format(_cancelErrMsgPattern, currentRetries), ex.Message); + } } } #endregion @@ -556,21 +574,24 @@ public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider prov }); Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + if(DataTestUtility.IsNotAzureSynapse()) { - using (SqlConnection cnn = new SqlConnection(cnnString)) - using (SqlCommand cmd = cnn.CreateCommand()) + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => { - cnn.Open(); - cmd.RetryLogicProvider = provider; - cmd.CommandText = query + " FOR XML AUTO"; - Assert.Throws(() => cmd.ExecuteXmlReader()); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); - + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) + { + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query + " FOR XML AUTO"; + Assert.Throws(() => cmd.ExecuteXmlReader()); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + } + retriesCount = 0; Parallel.For(0, concurrentExecution, i => @@ -618,20 +639,24 @@ public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider prov // TODO: there is a known issue by ExecuteXmlReaderAsync that should be solved first- issue #44 /* - retriesCount = 0; - Parallel.For(0, concurrentExecution, - i => + + if(DataTestUtility.IsNotAzureSynapse()) { - using (SqlConnection cnn = new SqlConnection(cnnString)) - using (SqlCommand cmd = cnn.CreateCommand()) + retriesCount = 0; + Parallel.For(0, concurrentExecution, + i => { - cnn.Open(); - cmd.RetryLogicProvider = provider; - cmd.CommandText = query + " FOR XML AUTO"; - Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()).Wait(); - } - }); - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + using (SqlConnection cnn = new SqlConnection(cnnString)) + using (SqlCommand cmd = cnn.CreateCommand()) + { + cnn.Open(); + cmd.RetryLogicProvider = provider; + cmd.CommandText = query + " FOR XML AUTO"; + Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()).Wait(); + } + }); + Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); + } */ } #endregion diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs index ca1bd1ce87..5691029f0c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs @@ -47,7 +47,10 @@ public void LoadValidInternalTypesAndEnableSwitch(string method1, string method2 // check the retry in action s_connectionCRLTest.ConnectionRetryOpenInvalidCatalogFailed(TcpCnnString, cnnProvider); s_commandCRLTest.RetryExecuteFail(TcpCnnString, cmdProvider); - s_commandCRLTest.RetryExecuteUnauthorizedSqlStatementDML(TcpCnnString, cmdProvider); + if (DataTestUtility.IsNotAzureSynapse()) + { + s_commandCRLTest.RetryExecuteUnauthorizedSqlStatementDML(TcpCnnString, cmdProvider); + } } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] @@ -245,7 +248,7 @@ public void InvalidTransientError(string errors) Assert.Equal(typeof(System.Configuration.ConfigurationErrorsException), ex.InnerException?.GetType()); Assert.Equal(typeof(ArgumentException), ex.InnerException?.InnerException?.GetType()); } -#endregion + #endregion #region AppContextSwitchManager [Theory] @@ -289,9 +292,12 @@ private void TestCommandExecute(SqlRetryLogicBaseProvider provider, RetryLogicCo Assert.Equal(cnfig.NumberOfTries, ex.InnerExceptions.Count); ex = Assert.Throws(() => cmd.ExecuteNonQuery()); Assert.Equal(cnfig.NumberOfTries, ex.InnerExceptions.Count); - cmd.CommandText = cmd.CommandText + " FOR XML AUTO"; - ex = Assert.Throws(() => cmd.ExecuteXmlReader()); - Assert.Equal(cnfig.NumberOfTries, ex.InnerExceptions.Count); + if (DataTestUtility.IsNotAzureSynapse()) + { + cmd.CommandText = cmd.CommandText + " FOR XML AUTO"; + ex = Assert.Throws(() => cmd.ExecuteXmlReader()); + Assert.Equal(cnfig.NumberOfTries, ex.InnerExceptions.Count); + } } } @@ -312,9 +318,12 @@ private async Task TestCommandExecuteAsync(SqlRetryLogicBaseProvider provider, R Assert.Equal(cnfig.NumberOfTries, ex.InnerExceptions.Count); ex = await Assert.ThrowsAsync(() => cmd.ExecuteNonQueryAsync()); Assert.Equal(cnfig.NumberOfTries, ex.InnerExceptions.Count); - cmd.CommandText = cmd.CommandText + " FOR XML AUTO"; - ex = await Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()); - Assert.Equal(cnfig.NumberOfTries, ex.InnerExceptions.Count); + if (DataTestUtility.IsNotAzureSynapse()) + { + cmd.CommandText = cmd.CommandText + " FOR XML AUTO"; + ex = await Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()); + Assert.Equal(cnfig.NumberOfTries, ex.InnerExceptions.Count); + } } } #endregion diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs index 4debbf4eca..7e2e33db54 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs @@ -51,7 +51,7 @@ public void ConnectionCancelRetryOpenInvalidCatalog(string cnnString, SqlRetryLo [ActiveIssue(14590, TestPlatforms.Windows)] // avoid creating a new database in Azure - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyLongRunner), parameters: new object[] { 10 }, MemberType = typeof(RetryLogicTestHelper))] public void CreateDatabaseWhileTryingToConnect(string cnnString, SqlRetryLogicBaseProvider provider) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs index f5d8240053..9d1c5c6b5f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs @@ -47,7 +47,7 @@ public static void ReadNextQueryAfterTxAbortedPoolEnabled(string connString) => ReadNextQueryAfterTxAbortedTest(connString); // Azure SQL has no DTC support - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [MemberData(nameof(PoolDisabledConnectionStrings))] public static void ReadNextQueryAfterTxAbortedPoolDisabled(string connString) => ReadNextQueryAfterTxAbortedTest(connString); @@ -89,15 +89,18 @@ private static void ReadNextQueryAfterTxAbortedTest(string connString) } } - using (SqlConnection sqlConnection = new SqlConnection(connString)) - using (SqlCommand cmd = new SqlCommand("SELECT TOP(1) 4 Clm0 FROM sysobjects FOR XML AUTO", sqlConnection)) + if (DataTestUtility.IsNotAzureSynapse()) { - sqlConnection.Open(); - using (System.Xml.XmlReader reader = cmd.ExecuteXmlReader()) + using (SqlConnection sqlConnection = new SqlConnection(connString)) + using (SqlCommand cmd = new SqlCommand("SELECT TOP(1) 4 Clm0 FROM sysobjects FOR XML AUTO", sqlConnection)) { - bool result = reader.Read(); - Assert.True(result); - Assert.Equal("4", reader[0]); + sqlConnection.Open(); + using (System.Xml.XmlReader reader = cmd.ExecuteXmlReader()) + { + bool result = reader.Read(); + Assert.True(result); + Assert.Equal("4", reader[0]); + } } } } From e9f2734b11b076e25e088d1cb142bc045f2f768b Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 4 Jun 2021 13:40:17 -0700 Subject: [PATCH 61/87] Fix Vulnerability (#1100) --- ...ta.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj | 1 + tools/specs/Microsoft.Data.SqlClient.nuspec | 5 +++++ ...ta.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj index 5e422ed108..bf6e29cf20 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj @@ -24,6 +24,7 @@ + diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index d8df8ccf98..8008024b94 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -30,6 +30,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -44,6 +45,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -57,6 +59,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -70,6 +73,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -84,6 +88,7 @@ When using NuGet 3.x this package requires at least version 3.4. + diff --git a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec index 6b40910512..9667ff7fd0 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -27,18 +27,21 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti + + + From b672ea74c7e52c2fee215ee14f4a54acbdb719a1 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 7 Jun 2021 15:56:17 -0700 Subject: [PATCH 62/87] Update SNI version to stable v3.0.0. (#1102) --- tools/props/Versions.props | 4 ++-- tools/specs/Microsoft.Data.SqlClient.nuspec | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 99b015718c..30f2061951 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -11,7 +11,7 @@ - 3.0.0-preview1.21104.2 + 3.0.0 4.3.1 4.3.0 @@ -29,7 +29,7 @@ 4.7.0 - 3.0.0-preview1.21104.2 + 3.0.0 4.7.0 4.7.0 4.7.0 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 8008024b94..e8da7d17ae 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -28,7 +28,7 @@ When using NuGet 3.x this package requires at least version 3.4. sqlclient microsoft.data.sqlclient - + @@ -37,7 +37,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -51,7 +51,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -65,7 +65,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -80,7 +80,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + From a51a67453c27c81816d90e15b6100c308108840c Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Mon, 7 Jun 2021 16:01:28 -0700 Subject: [PATCH 63/87] Add support ConfigurationManager in Net Standard for configurable retry logic (#1090) --- .../src/Microsoft.Data.SqlClient.csproj | 20 +++---- ...nfigurableRetryLogicManager.NetStandard.cs | 28 ---------- .../SqlAppContextSwitchManager.NetCoreApp.cs | 0 .../Microsoft/Data/SqlClient/SqlCommand.cs | 15 +----- .../Microsoft/Data/SqlClient/SqlConnection.cs | 15 +----- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 15 +----- .../Microsoft/Data/SqlClient/SqlConnection.cs | 15 +----- .../Data/SqlClient/LocalAppContextSwitches.cs | 37 ++++++++++++- .../Common/SqlRetryLogicProvider.cs | 3 -- ....cs => SqlConfigurableRetryLogicLoader.cs} | 52 ++++++++----------- .../SqlConfigurableRetryLogicManager.cs | 42 +++++---------- ....Data.SqlClient.ManualTesting.Tests.csproj | 4 +- .../SQL/RetryLogic/RetryLogicConfigHelper.cs | 10 ---- .../SQL/RetryLogic/RetryLogicTestHelper.cs | 7 +++ .../SqlConfigurationManagerReliabilityTest.cs | 6 +++ tools/props/Versions.props | 4 ++ tools/specs/Microsoft.Data.SqlClient.nuspec | 2 + 18 files changed, 108 insertions(+), 171 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.NetStandard.cs rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{Reliability => }/SqlAppContextSwitchManager.NetCoreApp.cs (100%) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/{SqlConfigurableRetryLogicManager.CreateProvider.cs => SqlConfigurableRetryLogicLoader.cs} (90%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index ff1672c097..f4f09ef5ea 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -310,6 +310,14 @@ Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicLoader.cs + + + Microsoft\Data\SqlClient\Reliability\AppConfigManager.cs + + + Microsoft\Data\SqlClient\SqlUtil.cs @@ -334,7 +342,6 @@ - @@ -383,14 +390,6 @@ - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.CreateProvider.NetCoreApp.cs - - - Microsoft\Data\SqlClient\Reliability\AppConfigManager.NetCoreApp.cs - - - @@ -838,7 +837,7 @@ - + @@ -857,6 +856,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.NetStandard.cs deleted file mode 100644 index 7d01bf7713..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.NetStandard.cs +++ /dev/null @@ -1,28 +0,0 @@ -// 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; - -namespace Microsoft.Data.SqlClient -{ - /// - /// Configurable retry logic manager; - /// Receive the default providers by a loader and feeds the connections and commands. - /// - internal sealed partial class SqlConfigurableRetryLogicManager - { - private static readonly Lazy s_loader = - new Lazy(() => new SqlConfigurableRetryLogicLoader()); - } - /// - /// Configurable retry logic loader - /// - internal sealed partial class SqlConfigurableRetryLogicLoader - { - public SqlConfigurableRetryLogicLoader() - { - AssignProviders(); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlAppContextSwitchManager.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAppContextSwitchManager.NetCoreApp.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlAppContextSwitchManager.NetCoreApp.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAppContextSwitchManager.NetCoreApp.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index e4b71d6eb2..24a82db701 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -493,20 +493,7 @@ private SqlInternalConnectionTds InternalTdsConnection } } - private bool? _isRetryEnabled; - private bool IsRetryEnabled - { - get - { - if (_isRetryEnabled == null) - { - bool result; - result = AppContext.TryGetSwitch(SqlRetryLogicProvider.EnableRetryLogicSwitch, out result) ? result : false; - _isRetryEnabled = result; - } - return (bool)_isRetryEnabled; - } - } + private static bool IsRetryEnabled => LocalAppContextSwitches.IsRetryEnabled; /// public SqlRetryLogicBaseProvider RetryLogicProvider diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 5ce75ba598..6bdc8cf6f9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -111,20 +111,7 @@ private enum CultureCheckState : uint private static readonly Action s_openAsyncCancel = OpenAsyncCancel; private static readonly Action, object> s_openAsyncComplete = OpenAsyncComplete; - private bool? _isRetryEnabled; - private bool IsRetryEnabled - { - get - { - if (_isRetryEnabled == null) - { - bool result; - result = AppContext.TryGetSwitch(SqlRetryLogicProvider.EnableRetryLogicSwitch, out result) ? result : false; - _isRetryEnabled = result; - } - return (bool)_isRetryEnabled; - } - } + private static bool IsRetryEnabled => LocalAppContextSwitches.IsRetryEnabled; /// public SqlRetryLogicBaseProvider RetryLogicProvider diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index a0bf1d5a51..61cc77db4f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -401,8 +401,8 @@ Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.CreateProvider.cs + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicLoader.cs Microsoft\Data\SqlClient\Reliability\AppConfigManager.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index bb0264666f..b0f8dc3d50 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -616,20 +616,7 @@ private bool IsShiloh } } - private bool? _isRetryEnabled; - private bool IsRetryEnabled - { - get - { - if (_isRetryEnabled == null) - { - bool result; - result = AppContext.TryGetSwitch(SqlRetryLogicProvider.EnableRetryLogicSwitch, out result) ? result : false; - _isRetryEnabled = result; - } - return (bool)_isRetryEnabled; - } - } + private static bool IsRetryEnabled => LocalAppContextSwitches.IsRetryEnabled; /// [ diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index ef0d91934e..af4b1a3fcf 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -310,20 +310,7 @@ internal List GetColumnEncryptionCustomKeyStoreProvidersNames() // Retry Logic private SqlRetryLogicBaseProvider _retryLogicProvider; - private bool? _isRetryEnabled; - private bool IsRetryEnabled - { - get - { - if (_isRetryEnabled == null) - { - bool result; - result = AppContext.TryGetSwitch(SqlRetryLogicProvider.EnableRetryLogicSwitch, out result) ? result : false; - _isRetryEnabled = result; - } - return (bool)_isRetryEnabled; - } - } + private static bool IsRetryEnabled => LocalAppContextSwitches.IsRetryEnabled; /// [ diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs index 14084f92b0..9d2111ac24 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs @@ -3,17 +3,52 @@ // See the LICENSE file in the project root for more information. using System; +using System.Reflection; using System.Runtime.CompilerServices; namespace Microsoft.Data.SqlClient { internal static partial class LocalAppContextSwitches { + private const string TypeName = nameof(LocalAppContextSwitches); internal const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking"; internal const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"; + // safety switch + internal const string EnableRetryLogicSwitch = "Switch.Microsoft.Data.SqlClient.EnableRetryLogic"; private static bool _makeReadAsyncBlocking; private static bool? s_LegacyRowVersionNullBehavior; + private static bool? s_isRetryEnabled = null; + +#if !NETFRAMEWORK + static LocalAppContextSwitches() + { + IAppContextSwitchOverridesSection appContextSwitch = AppConfigManager.FetchConfigurationSection(AppContextSwitchOverridesSection.Name); + try + { + SqlAppContextSwitchManager.ApplyContextSwitches(appContextSwitch); + } + catch (Exception e) + { + // Don't throw an exception for an invalid config file + SqlClientEventSource.Log.TryTraceEvent(": {2}", TypeName, MethodBase.GetCurrentMethod().Name, e); + } + } +#endif + + internal static bool IsRetryEnabled + { + get + { + if (s_isRetryEnabled is null) + { + bool result; + result = AppContext.TryGetSwitch(EnableRetryLogicSwitch, out result) ? result : false; + s_isRetryEnabled = result; + } + return s_isRetryEnabled.Value; + } + } public static bool MakeReadAsyncBlocking { @@ -33,7 +68,7 @@ public static bool LegacyRowVersionNullBehavior { get { - if (s_LegacyRowVersionNullBehavior == null) + if (s_LegacyRowVersionNullBehavior is null) { bool value = false; if (AppContext.TryGetSwitch(LegacyRowVersionNullString, out bool providedValue)) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/Common/SqlRetryLogicProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/Common/SqlRetryLogicProvider.cs index cee9b4a24d..c865c2870c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/Common/SqlRetryLogicProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/Common/SqlRetryLogicProvider.cs @@ -19,9 +19,6 @@ internal class SqlRetryLogicProvider : SqlRetryLogicBaseProvider // keeps free RetryLogic objects private readonly ConcurrentBag _retryLogicPool = new ConcurrentBag(); - // safety switch for the preview version - internal const string EnableRetryLogicSwitch = "Switch.Microsoft.Data.SqlClient.EnableRetryLogic"; - /// Creates an instance of this type. public SqlRetryLogicProvider(SqlRetryLogicBase retryLogic) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.CreateProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicLoader.cs similarity index 90% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.CreateProvider.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicLoader.cs index f37d3ef3d5..838dd9f9da 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.CreateProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicLoader.cs @@ -9,37 +9,6 @@ namespace Microsoft.Data.SqlClient { - /// - /// Configurable retry logic manager; - /// Receive the default providers by a loader and feeds the connections and commands. - /// - internal sealed partial class SqlConfigurableRetryLogicManager - { - private static readonly Lazy s_loader = - new Lazy(() => - { - ISqlConfigurableRetryConnectionSection cnnConfig = null; - ISqlConfigurableRetryCommandSection cmdConfig = null; - - // Fetch the section attributes values from the configuration section of the app config file. - cnnConfig = AppConfigManager.FetchConfigurationSection(SqlConfigurableRetryConnectionSection.Name); - cmdConfig = AppConfigManager.FetchConfigurationSection(SqlConfigurableRetryCommandSection.Name); -#if !NETFRAMEWORK - IAppContextSwitchOverridesSection appContextSwitch = AppConfigManager.FetchConfigurationSection(AppContextSwitchOverridesSection.Name); - try - { - SqlAppContextSwitchManager.ApplyContextSwitches(appContextSwitch); - } - catch (Exception e) - { - // Don't throw an exception for an invalid config file - SqlClientEventSource.Log.TryTraceEvent(": {2}", TypeName, MethodBase.GetCurrentMethod().Name, e); - } -#endif - return new SqlConfigurableRetryLogicLoader(cnnConfig, cmdConfig); - }); - } - /// /// Configurable retry logic loader /// This class shouldn't throw exceptions; @@ -47,6 +16,27 @@ internal sealed partial class SqlConfigurableRetryLogicManager /// internal sealed partial class SqlConfigurableRetryLogicLoader { + private const string TypeName = nameof(SqlConfigurableRetryLogicLoader); + + /// + /// The default non retry provider will apply if a parameter passes by null. + /// + private void AssignProviders(SqlRetryLogicBaseProvider cnnProvider = null, SqlRetryLogicBaseProvider cmdProvider = null) + { + ConnectionProvider = cnnProvider ?? SqlConfigurableRetryFactory.CreateNoneRetryProvider(); + CommandProvider = cmdProvider ?? SqlConfigurableRetryFactory.CreateNoneRetryProvider(); + } + + /// + /// Default Retry provider for SqlConnections + /// + internal SqlRetryLogicBaseProvider ConnectionProvider { get; private set; } + + /// + /// Default Retry provider for SqlCommands + /// + internal SqlRetryLogicBaseProvider CommandProvider { get; private set; } + public SqlConfigurableRetryLogicLoader(ISqlConfigurableRetryConnectionSection connectionRetryConfigs, ISqlConfigurableRetryCommandSection commandRetryConfigs, string cnnSectionName = SqlConfigurableRetryConnectionSection.Name, diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.cs index c0299de655..cac4c04645 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.cs @@ -11,10 +11,23 @@ namespace Microsoft.Data.SqlClient /// Configurable retry logic manager; /// Receive the default providers by a loader and feeds connections and commands. /// - internal sealed partial class SqlConfigurableRetryLogicManager + internal sealed class SqlConfigurableRetryLogicManager { private const string TypeName = nameof(SqlConfigurableRetryLogicManager); + private static readonly Lazy s_loader = + new Lazy(() => + { + ISqlConfigurableRetryConnectionSection cnnConfig = null; + ISqlConfigurableRetryCommandSection cmdConfig = null; + + // Fetch the section attributes values from the configuration section of the app config file. + cnnConfig = AppConfigManager.FetchConfigurationSection(SqlConfigurableRetryConnectionSection.Name); + cmdConfig = AppConfigManager.FetchConfigurationSection(SqlConfigurableRetryCommandSection.Name); + + return new SqlConfigurableRetryLogicLoader(cnnConfig, cmdConfig); + }); + private SqlConfigurableRetryLogicManager() {/*prevent external object creation*/} /// @@ -75,33 +88,6 @@ internal static SqlRetryLogicBaseProvider CommandProvider } - /// - /// Configurable retry logic loader - /// - internal sealed partial class SqlConfigurableRetryLogicLoader - { - private const string TypeName = nameof(SqlConfigurableRetryLogicLoader); - - /// - /// The default non retry provider will apply if a parameter passes by null. - /// - private void AssignProviders(SqlRetryLogicBaseProvider cnnProvider = null, SqlRetryLogicBaseProvider cmdProvider = null) - { - ConnectionProvider = cnnProvider ?? SqlConfigurableRetryFactory.CreateNoneRetryProvider(); - CommandProvider = cmdProvider ?? SqlConfigurableRetryFactory.CreateNoneRetryProvider(); - } - - /// - /// Default Retry provider for SqlConnections - /// - internal SqlRetryLogicBaseProvider ConnectionProvider { get; private set; } - - /// - /// Default Retry provider for SqlCommands - /// - internal SqlRetryLogicBaseProvider CommandProvider { get; private set; } - } - internal interface IAppContextSwitchOverridesSection { string Value { get; set; } 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 d70b91d864..4fb650e481 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 @@ -24,7 +24,6 @@ - @@ -75,6 +74,7 @@ + @@ -297,12 +297,12 @@ - + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs index 7cf4e196c9..f1ad1c98f8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs @@ -30,16 +30,6 @@ public class RetryLogicConfigHelper public const string DefaultTansientErrors = "1204, 1205, 1222, 49918, 49919, 49920, 4060, 4221, 40143, 40613, 40501, 40540, 40197, 10929, 10928, 10060, 10054, 10053, 997, 233, 64, 20, 0, -2, 207, 102, 2812"; - //private const string SqlRetryLogicProviderTypeName = "Microsoft.Data.SqlClient.SqlRetryLogicProvider"; - //private const string SqlExponentialIntervalEnumeratorTypeName = "Microsoft.Data.SqlClient.SqlExponentialIntervalEnumerator"; - //private const string SqlIncrementalIntervalEnumeratorTypeName = "Microsoft.Data.SqlClient.SqlIncrementalIntervalEnumerator"; - //private const string SqlFixedIntervalEnumeratorTypeName = "Microsoft.Data.SqlClient.SqlFixedIntervalEnumerator"; - //private const string SqlNoneIntervalEnumeratorTypeName = "Microsoft.Data.SqlClient.SqlNoneIntervalEnumerator"; - - //private static readonly Type s_sqlRetryLogicBaseProviderType = typeof(SqlRetryLogicBaseProvider); - //private static readonly Type s_sqlRetryLogicProviderType = s_sqlClientAssembly.GetType(SqlRetryLogicProviderTypeName); - //private static readonly Type s_sqlRetryLogicBaseType = s_sqlClientAssembly.GetType(SqlRetryLogicBaseTypeName); - private static readonly Random s_random = new Random(); private static readonly Assembly s_sqlClientAssembly = typeof(SqlConnection).Assembly; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs index 169d24d03a..5f5d1fe82e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Text.RegularExpressions; @@ -45,6 +46,9 @@ public enum FilterSqlStatements public class RetryLogicTestHelper { internal const string RetryAppContextSwitch = "Switch.Microsoft.Data.SqlClient.EnableRetryLogic"; + private static readonly Assembly s_sqlClientAssembly = typeof(SqlConnection).Assembly; + private static readonly Type s_LocalAppContextSwitchesType = s_sqlClientAssembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches"); + private static readonly FieldInfo s_isRetryEnabledFieldInfo = s_LocalAppContextSwitchesType.GetField("s_isRetryEnabled", BindingFlags.Static | BindingFlags.NonPublic); private static readonly HashSet s_defaultTransientErrors = new HashSet @@ -79,8 +83,11 @@ public class RetryLogicTestHelper internal static readonly string s_ExceedErrMsgPattern = SystemDataResourceManager.Instance.SqlRetryLogic_RetryExceeded; internal static readonly string s_CancelErrMsgPattern = SystemDataResourceManager.Instance.SqlRetryLogic_RetryCanceled; + public static void CleanRetryEnabledCache() => s_isRetryEnabledFieldInfo.SetValue(null, null); + public static void SetRetrySwitch(bool value) { + CleanRetryEnabledCache(); AppContext.SetSwitch(RetryAppContextSwitch, value); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs index 5691029f0c..f5594069cf 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs @@ -45,6 +45,7 @@ public void LoadValidInternalTypesAndEnableSwitch(string method1, string method2 RetryLogicConfigHelper.AssessProvider(cmdProvider, cmdCfg, switchValue); // check the retry in action + RetryLogicTestHelper.CleanRetryEnabledCache(); s_connectionCRLTest.ConnectionRetryOpenInvalidCatalogFailed(TcpCnnString, cnnProvider); s_commandCRLTest.RetryExecuteFail(TcpCnnString, cmdProvider); if (DataTestUtility.IsNotAzureSynapse()) @@ -68,6 +69,7 @@ public void LoadValidInternalTypesWithoutEnablingSwitch(string method1, string m RetryLogicConfigHelper.AssessProvider(cnnProvider, cnnCfg, switchValue); RetryLogicConfigHelper.AssessProvider(cmdProvider, cmdCfg, switchValue); + RetryLogicTestHelper.CleanRetryEnabledCache(); s_connectionCRLTest.DefaultOpenWithoutRetry(TcpCnnString, cnnProvider); s_commandCRLTest.NoneRetriableExecuteFail(TcpCnnString, cmdProvider); } @@ -98,6 +100,7 @@ public void LoadCustomMethod(string typeName, string methodName) object loaderObj = RetryLogicConfigHelper.ReturnLoaderAndProviders(cnnCfg, cmdCfg, switchValue, out SqlRetryLogicBaseProvider cnnProvider, out SqlRetryLogicBaseProvider cmdProvider); Assert.NotNull(loaderObj); + RetryLogicTestHelper.CleanRetryEnabledCache(); TestConnection(cnnProvider, cnnCfg); TestCommandExecute(cmdProvider, cmdCfg); TestCommandExecuteAsync(cmdProvider, cmdCfg).Wait(); @@ -130,6 +133,7 @@ public void LoadInvalidCustomRetryLogicType(string typeName, string methodName) object loaderObj = RetryLogicConfigHelper.ReturnLoaderAndProviders(cnnCfg, cmdCfg, switchValue, out SqlRetryLogicBaseProvider cnnProvider, out SqlRetryLogicBaseProvider cmdProvider); Assert.NotNull(loaderObj); + RetryLogicTestHelper.CleanRetryEnabledCache(); s_connectionCRLTest.DefaultOpenWithoutRetry(TcpCnnString, cnnProvider); s_commandCRLTest.NoneRetriableExecuteFail(TcpCnnString, cmdProvider); } @@ -151,6 +155,7 @@ public void InvalidRetryMethodName(string methodName) Assert.NotNull(loaderObj); // none retriable logic applies. + RetryLogicTestHelper.CleanRetryEnabledCache(); s_connectionCRLTest.DefaultOpenWithoutRetry(TcpCnnString, cnnProvider); s_commandCRLTest.NoneRetriableExecuteFail(TcpCnnString, cmdProvider); } @@ -177,6 +182,7 @@ public void InvalidRetryLogicTypeWithValidInternalMethodName(string typeName) RetryLogicConfigHelper.AssessProvider(cmdProvider, cmdCfg, switchValue); // internal type used to resolve the specified method + RetryLogicTestHelper.CleanRetryEnabledCache(); s_connectionCRLTest.ConnectionRetryOpenInvalidCatalogFailed(TcpCnnString, cnnProvider); s_commandCRLTest.RetryExecuteFail(TcpCnnString, cmdProvider); } diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 30f2061951..3f667b8c65 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -44,6 +44,10 @@ 4.3.0 4.3.1 + + + 4.3.0 + [1.6.0,2.0.0) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index e8da7d17ae..8f179ccc9e 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -78,6 +78,7 @@ When using NuGet 3.x this package requires at least version 3.4. + @@ -93,6 +94,7 @@ When using NuGet 3.x this package requires at least version 3.4. + From 3df7de613aecee3a8b5229333c84142e6e83ae9d Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Mon, 7 Jun 2021 19:04:50 -0700 Subject: [PATCH 64/87] Give system key store providers precedence over instance-level providers (#1101) --- .../Microsoft.Data.SqlClient/SqlCommand.xml | 4 +- .../SqlConnection.xml | 6 ++- .../Microsoft/Data/SqlClient/SqlConnection.cs | 16 ++++---- .../Microsoft/Data/SqlClient/SqlConnection.cs | 18 ++++---- .../Data/SqlClient/SqlSecurityUtility.cs | 19 ++++++++- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 41 +++++++++++++++++++ 6 files changed, 82 insertions(+), 22 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index 4cd8b3529b..68b10ce395 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -2798,7 +2798,9 @@ Custom master key store providers can be registered with the driver at three dif Once any key store provider is found at a registration level, the driver will **NOT** fall back to the other registrations to search for a provider. If providers are registered but the proper provider is not found at a level, an exception will be thrown containing only the registered providers in the registration that was checked. -The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. No providers should be registered on the connection or command instances if one of the built-in column master key store providers is needed. +The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. + +This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index db0f670ea4..5942a41959 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -1026,7 +1026,7 @@ GO Registers the column encryption key store providers. This function should only be called once in an app. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. - The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. No providers should be registered on the connection or command instances if one of the built-in column master key store providers is needed. + The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 6bdc8cf6f9..09b62042f8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -212,10 +212,15 @@ private SqlConnection(SqlConnection connection) CacheConnectionStringProperties(); } + internal static bool TryGetSystemColumnEncryptionKeyStoreProvider(string keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider) + { + return s_systemColumnEncryptionKeyStoreProviders.TryGetValue(keyStoreName, out provider); + } + /// - /// This function walks through both system and custom column encryption key store providers and returns an object if found. + /// This function walks through both instance-level and global custom column encryption key store providers and returns an object if found. /// - /// Provider Name to be searched in System Provider dictionary and Custom provider dictionary. + /// Provider Name to be searched for. /// If the provider is found, initializes the corresponding SqlColumnEncryptionKeyStoreProvider instance. /// true if the provider is found, else returns false internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) @@ -227,17 +232,12 @@ internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out Sq return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); } - // Search in the sytem provider list. - if (s_systemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider)) - { - return true; - } - lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false if (s_globalCustomColumnEncryptionKeyStoreProviders is null) { + columnKeyStoreProvider = null; return false; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index af4b1a3fcf..cf77f152d6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -213,11 +213,16 @@ private static void ValidateCustomProviders(IDictionary - /// This function walks through both system and custom column encryption key store providers and returns an object if found. + /// This function walks through both instance-level and global custom column encryption key store providers and returns an object if found. /// - /// Provider Name to be searched in System Provider diction and Custom provider dictionary. - /// If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance. + /// Provider Name to be searched for. + /// If the provider is found, initializes the corresponding SqlColumnEncryptionKeyStoreProvider instance. /// true if the provider is found, else returns false internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) { @@ -228,17 +233,12 @@ internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out Sq return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); } - // Search in the system provider list. - if (s_systemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider)) - { - return true; - } - lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false if (s_globalCustomColumnEncryptionKeyStoreProviders is null) { + columnKeyStoreProvider = null; return false; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs index 2077590e77..67535743af 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Security.Cryptography; using System.Text; +using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { @@ -274,7 +275,7 @@ internal static void DecryptSymmetricKey(SqlTceCipherInfoEntry sqlTceCipherInfoE { try { - sqlClientSymmetricKey = InstanceLevelProvidersAreRegistered(connection, command) ? + sqlClientSymmetricKey = ShouldUseInstanceLevelProviderFlow(keyInfo.keyStoreName, connection, command) ? GetKeyFromLocalProviders(keyInfo, connection, command) : globalCekCache.GetKey(keyInfo, connection, command); encryptionkeyInfoChosen = keyInfo; @@ -367,7 +368,7 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string GetListOfProviderNamesThatWereSearched(connection, command)); } - if (InstanceLevelProvidersAreRegistered(connection, command)) + if (ShouldUseInstanceLevelProviderFlow(keyStoreName,connection, command)) { isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, CMKSignature); } @@ -399,6 +400,15 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string } } + // Instance-level providers will be used if at least one is registered on a connection or command and + // the required provider is not a system provider. System providers are pre-registered globally and + // must use the global provider flow + private static bool ShouldUseInstanceLevelProviderFlow(string keyStoreName, SqlConnection connection, SqlCommand command) + { + return InstanceLevelProvidersAreRegistered(connection, command) && + !keyStoreName.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix); + } + private static bool InstanceLevelProvidersAreRegistered(SqlConnection connection, SqlCommand command) => connection.HasColumnEncryptionKeyStoreProvidersRegistered || (command is not null && command.HasColumnEncryptionKeyStoreProvidersRegistered); @@ -423,6 +433,11 @@ internal static bool TryGetColumnEncryptionKeyStoreProvider(string keyStoreName, { Debug.Assert(!string.IsNullOrWhiteSpace(keyStoreName), "Provider name is invalid"); + if (SqlConnection.TryGetSystemColumnEncryptionKeyStoreProvider(keyStoreName, out provider)) + { + return true; + } + // command may be null because some callers do not have a command object, eg SqlBulkCopy if (command is not null && command.HasColumnEncryptionKeyStoreProvidersRegistered) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 31264fa396..b92a642ae8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -2242,6 +2242,38 @@ public void TestCommandCustomKeyStoreProviderDuringAeQuery(string connectionStri } } + // On Windows, "_fixture" will be type SQLSetupStrategyCertStoreProvider + // On non-Windows, "_fixture" will be type SQLSetupStrategyAzureKeyVault + // Test will pass on both but only SQLSetupStrategyCertStoreProvider is a system provider + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestSystemProvidersHavePrecedenceOverInstanceLevelProviders(string connectionString) + { + Dictionary customKeyStoreProviders = new() + { + { + SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, + new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential()) + } + }; + + using (SqlConnection connection = new(connectionString)) + { + connection.Open(); + using SqlCommand command = CreateCommandThatRequiresSystemKeyStoreProvider(connection); + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders); + command.ExecuteReader(); + } + + using (SqlConnection connection = new(connectionString)) + { + connection.Open(); + using SqlCommand command = CreateCommandThatRequiresSystemKeyStoreProvider(connection); + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customKeyStoreProviders); + command.ExecuteReader(); + } + } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.EnclaveEnabled))] [ClassData(typeof(AEConnectionStringProvider))] public void TestRetryWhenAEParameterMetadataCacheIsStale(string connectionString) @@ -2318,6 +2350,15 @@ private SqlCommand CreateCommandThatRequiresCustomKeyStoreProvider(SqlConnection return command; } + private SqlCommand CreateCommandThatRequiresSystemKeyStoreProvider(SqlConnection connection) + { + SqlCommand command = new( + $"SELECT * FROM [{_fixture.CustomKeyStoreProviderTestTable.Name}] WHERE FirstName = @firstName", + connection, null, SqlCommandColumnEncryptionSetting.Enabled); + command.Parameters.AddWithValue("firstName", "abc"); + return command; + } + private void AssertExceptionCausedByFailureToDecrypt(Exception ex) { Assert.Contains(_failedToDecryptMessage, ex.Message); From 8908b9259f81ce20e25eb2c900be12e66eb59832 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Wed, 9 Jun 2021 10:57:56 -0700 Subject: [PATCH 65/87] 3.0 GA release notes (#1107) --- CHANGELOG.md | 14 ++ release-notes/3.0/3.0.0.md | 343 ++++++++++++++++++++++++++++++++++++ release-notes/3.0/3.0.md | 6 + release-notes/3.0/README.md | 6 + release-notes/README.md | 2 +- 5 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 release-notes/3.0/3.0.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 95ed0bc4f8..5ffb996468 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +## [Stable Release 3.0.0] - 2021-06-09 + +### Added +- Added support for column encryption key caching when the server supports retrying queries that require enclave computations [#1062](https://github.com/dotnet/SqlClient/pull/1062) +- Added support for configurable retry logic configuration file in .NET Standard [#1090](https://github.com/dotnet/SqlClient/pull/1090) + +### Changed +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v3.0.0` [#1102](https://github.com/dotnet/SqlClient/pull/1102) +- Improved event counter display information [#1091](https://github.com/dotnet/SqlClient/pull/1091) + +### Breaking Changes +- Modified column encryption key store provider registrations to give built-in system providers precedence over providers registered on connection and command instances. [#1101](https://github.com/dotnet/SqlClient/pull/1101) + + ## [Stable Release 2.1.3] - 2021-05-21 ### Fixed diff --git a/release-notes/3.0/3.0.0.md b/release-notes/3.0/3.0.0.md new file mode 100644 index 0000000000..8b91f2742b --- /dev/null +++ b/release-notes/3.0/3.0.0.md @@ -0,0 +1,343 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.0.0 released 09 June 2021 + +This update brings the below changes over the previous preview release: + +### Added + +- Added support for column encryption key caching when the server supports retrying queries that require enclave computations [#1062](https://github.com/dotnet/SqlClient/pull/1062) +- Added support for configurable retry logic configuration file in .NET Standard [#1090](https://github.com/dotnet/SqlClient/pull/1090) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v3.0.0` [#1102](https://github.com/dotnet/SqlClient/pull/1102) +- Improved event counter display information [#1091](https://github.com/dotnet/SqlClient/pull/1091) + +### Breaking Changes + +- Modified column encryption key store provider registrations to give built-in system providers precedence over providers registered on connection and command instances. [#1101](https://github.com/dotnet/SqlClient/pull/1101) + +## Summary of changes in 3.0 + +All changes in Microsoft.Data.SqlClient v3.0 over v2.1: + +### New Additions + +- Added support for Configurable Retry Logic [#693](https://github.com/dotnet/SqlClient/pull/693) [#966](https://github.com/dotnet/SqlClient/pull/966) [Read more](#configurable-retry-logic) +- Added support for Event counters in .NET Core 3.1+ and .NET Standard 2.1+ [#719](https://github.com/dotnet/SqlClient/pull/719) [Read more](#event-counters) +- Added support for Assembly Context Unloading in .NET Core [#913](https://github.com/dotnet/SqlClient/pull/913) +- Added missing `System.Runtime.Caching` dependency for .NET Standard assemblies [#877](https://github.com/dotnet/SqlClient/pull/877) +- **Microsoft.Data.SqlClient** now depends on **Azure.Identity** library to acquire a token for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. [#1010](https://github.com/dotnet/SqlClient/pull/1010) [Read more](#azure-identity-dependency-introduction) +- Upgraded Native SNI dependency to **v3.0.0-preview1** along with enhanced event tracing support [#1006](https://github.com/dotnet/SqlClient/pull/1006) [Read more](#event-tracing-improvements-in-sni.dll) +- Added support for "Active Directory Default" authentication mode [#1043](https://github.com/dotnet/SqlClient/pull/1043) [Read more](#active-directory-default-authentication-support) +- Added support for connection-level and command-level registration of custom key store providers to enable multi-tenant applications to control key store access [#1045](https://github.com/dotnet/SqlClient/pull/1045) [#1056](https://github.com/dotnet/SqlClient/pull/1056) [#1078](https://github.com/dotnet/SqlClient/pull/1078) [Read more](#custom-master-key-store-provider-registration-enhancements) +- Added IP address preference support for TCP connections [#1015](https://github.com/dotnet/SqlClient/pull/1015) [Read more](#ip-address-preference) + +### Bug Fixes + +- Fixed wrong results issues by changing the timeout timer to ensure a correct execution state [#906](https://github.com/dotnet/SqlClient/pull/906) +- Fixed Kerberos authentication issues when configured Server Principal Name (SPN) didn't contain default port [#930](https://github.com/dotnet/SqlClient/pull/930) +- Fixed MARS header errors when `MakeReadAsyncBlocking` App Context switch is set to `false` [#910](https://github.com/dotnet/SqlClient/pull/910) [#922](https://github.com/dotnet/SqlClient/pull/922) +- Fixed unwanted exceptions being thrown from `SqlDataReader.Dispose` [#920](https://github.com/dotnet/SqlClient/pull/920) +- Fixed issues connecting to SQL Server instance with instance name specified from Unix environment [#870](https://github.com/dotnet/SqlClient/pull/870) +- Fixed TCP Keep Alive issues in .NET Core [#854](https://github.com/dotnet/SqlClient/pull/854) +- Fixed Kerberos Authentication issues caused due to regression [#845](https://github.com/dotnet/SqlClient/pull/845) +- Fixed issues with System-Assigned Managed Identity in Azure Functions [#829](https://github.com/dotnet/SqlClient/pull/829) +- Fixed missing error messages in Managed SNI [#882](https://github.com/dotnet/SqlClient/pull/882) +- Fixed event source trace string issue [#940](https://github.com/dotnet/SqlClient/pull/940) +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set [#1023](https://github.com/dotnet/SqlClient/pull/1023) +- Fixed derived parameters containing incorrect typename [#1020](https://github.com/dotnet/SqlClient/pull/1020) +- Fixed server connection leak possibilities when an exception occurs in pooling layer [#890](https://github.com/dotnet/SqlClient/pull/890) +- Fixed IP connection resolving logic in .NET Core [#1016](https://github.com/dotnet/SqlClient/pull/1016) [#1031](https://github.com/dotnet/SqlClient/pull/1031) +- Fixed corrupted connection issue when an exception occurs during RPC execution with TVP types [#1068](https://github.com/dotnet/SqlClient/pull/1068) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1042](https://github.com/dotnet/SqlClient/pull/1042) + +### Improvements and Changes + +- Changed App Context switch `MakeReadAsyncBlocking` default to `false` [#937](https://github.com/dotnet/SqlClient/pull/937) +- Replaced usage of `BinaryFormatter` with `DataContractSerializer` [#869](https://github.com/dotnet/SqlClient/pull/869) +- Prohibited `DtdProcessing` on `XmlTextReader` instance in .NET Core [#884](https://github.com/dotnet/SqlClient/pull/884) +- Improved performance by reducing memory allocations in `SerializeEncodingChar`/`WriteEncodingChar` and some options boxing [#785](https://github.com/dotnet/SqlClient/pull/785) +- Improved performance by preventing orphaned active packets being GC'ed without clear [#888](https://github.com/dotnet/SqlClient/pull/888) +- Various performance improvements [#889](https://github.com/dotnet/SqlClient/pull/889) [#900](https://github.com/dotnet/SqlClient/pull/900) +- Partial event source tracing improvements in .NET Core [#867](https://github.com/dotnet/SqlClient/pull/867) [#897](https://github.com/dotnet/SqlClient/pull/897) +- Changes to share common files between NetFx and NetCore source code [#827](https://github.com/dotnet/SqlClient/pull/827) [#835](https://github.com/dotnet/SqlClient/pull/835) [#838](https://github.com/dotnet/SqlClient/pull/838) [#881](https://github.com/dotnet/SqlClient/pull/881) +- Performance improvements in `SqlDateTime` to `DateTime` internal conversion method [#912](https://github.com/dotnet/SqlClient/pull/912) +- Improved memory allocation by avoiding unnecessary context switching [1008](https://github.com/dotnet/SqlClient/pull/1008) +- Updated `Microsoft.Identity.Client` version from **4.21.1** to **4.22.0** [#1036](https://github.com/dotnet/SqlClient/pull/1036) +- Various performance improvements [#963](https://github.com/dotnet/SqlClient/pull/963) [#996](https://github.com/dotnet/SqlClient/pull/996) [#1004](https://github.com/dotnet/SqlClient/pull/1004) [#1012](https://github.com/dotnet/SqlClient/pull/1012) [#1017](https://github.com/dotnet/SqlClient/pull/1017) +- Event source tracing improvements [#1018](https://github.com/dotnet/SqlClient/pull/1018) +- Changes to share common files between NetFx and NetCore source code [#871](https://github.com/dotnet/SqlClient/pull/871) [#887](https://github.com/dotnet/SqlClient/pull/887) +- Updated error messages for enclave exceptions to include a link to a troubleshooting guide. [#994](https://github.com/dotnet/SqlClient/pull/994) +- Changes to share common files between projects [#1022](https://github.com/dotnet/SqlClient/pull/1022) [#1038](https://github.com/dotnet/SqlClient/pull/1038) [#1040](https://github.com/dotnet/SqlClient/pull/1040) [#1033](https://github.com/dotnet/SqlClient/pull/1033) [#1028](https://github.com/dotnet/SqlClient/pull/1028) [#1039](https://github.com/dotnet/SqlClient/pull/1039) + +### Breaking Changes + +- The minimum supported .NET Framework version has been increased to v4.6.1. .NET Framework v4.6.0 is no longer supported. [#899](https://github.com/dotnet/SqlClient/pull/899) +- `User Id` connection property now requires `Client Id` instead of `Object Id` for **User-Assigned Managed Identity** [#1010](https://github.com/dotnet/SqlClient/pull/1010) [Read more](#azure-identity-dependency-introduction) +- `SqlDataReader` now returns a `DBNull` value instead of an empty `byte[]`. Legacy behavior can be enabled by setting `AppContext` switch **Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior** [#998](https://github.com/dotnet/SqlClient/pull/998) [Read more](#enabling-row-version-null-behavior) + +### Configurable Retry Logic + +This new feature introduces configurable support for client applications to retry on "transient" or "retriable" errors. Configuration can be done through code or app config files and retry operations can be applied to opening a connection or executing a command. This feature is disabled by default and is currently in preview. To enable this support, client applications must turn on the following safety switch: + +`AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.EnableRetryLogic", true);` + +Once the .NET AppContext switch is enabled, a retry logic policy can be defined for `SqlConnection` and `SqlCommand` independently, or together using various customization options. + +New public APIs are introduced in `SqlConnection` and `SqlCommand` for registering a custom `SqlRetryLogicBaseProvider` implementation: + +```cs +public SqlConnection +{ + public SqlRetryLogicBaseProvider RetryLogicProvider; +} + +public SqlCommand +{ + public SqlRetryLogicBaseProvider RetryLogicProvider; +} + +``` + +API Usage examples can be found here: +[SqlConnection retry sample](..\..\doc\samples\SqlConfigurableRetryLogic_OpenConnection.cs) +[SqlCommand retry sample](..\..\doc\samples\SqlConfigurableRetryLogic_SqlCommand.cs) +[Sample for retry logic options](..\..\doc\samples\SqlConfigurableRetryLogic_SqlRetryLogicOptions.cs) + +New configuration sections have also been introduced to do the same registration from configuration files, without having to modify existing code: + +```xml +
+ +
+``` + +A simple example of using the new configuration sections in configuration files is below: + +```xml + + + +
+
+ +
+ + + + + + + + + + + +``` + +Alternatively, applications can implement their own provider of the `SqlRetryLogicBaseProvider` base class, and register it with `SqlConnection`/`SqlCommand`. + +### Event Counters + +The following counters are now available for applications targeting .NET Core 3.1+ and .NET Standard 2.1+: + +|Name|Display name|Description| +|-------------------------|-----------------|-----------------| +|**active-hard-connections**|Actual active connections currently made to servers|The number of connections that are currently open to database servers.| +|**hard-connects**|Actual connection rate to servers|The number of connections per second that are being opened to database servers.| +|**hard-disconnects**|Actual disconnection rate from servers|The number of disconnects per second that are being made to database servers.| +|**active-soft-connects**|Active connections retrieved from the connection pool|The number of already-open connections being consumed from the connection pool.| +|**soft-connects**|Rate of connections retrieved from the connection pool|The number of connections per second that are being consumed from the connection pool.| +|**soft-disconnects**|Rate of connections returned to the connection pool|The number of connections per second that are being returned to the connection pool.| +|**number-of-non-pooled-connections**|Number of connections not using connection pooling|The number of active connections that aren't pooled.| +|**number-of-pooled-connections**|Number of connections managed by the connection pool|The number of active connections that are being managed by the connection pooling infrastructure.| +|**number-of-active-connection-pool-groups**|Number of active unique connection strings|The number of unique connection pool groups that are active. This counter is controlled by the number of unique connection strings that are found in the AppDomain.| +|**number-of-inactive-connection-pool-groups**|Number of unique connection strings waiting for pruning|The number of unique connection pool groups that are marked for pruning. This counter is controlled by the number of unique connection strings that are found in the AppDomain.| +|**number-of-active-connection-pools**|Number of active connection pools|The total number of connection pools.| +|**number-of-inactive-connection-pools**|Number of inactive connection pools|The number of inactive connection pools that haven't had any recent activity and are waiting to be disposed.| +|**number-of-active-connections**|Number of active connections|The number of active connections that are currently in use.| +|**number-of-free-connections**|Number of ready connections in the connection pool|The number of open connections available for use in the connection pools.| +|**number-of-stasis-connections**|Number of connections currently waiting to be ready|The number of connections currently awaiting completion of an action and which are unavailable for use by the application.| +|**number-of-reclaimed-connections**|Number of reclaimed connections from GC|The number of connections that have been reclaimed through garbage collection where `Close` or `Dispose` wasn't called by the application. **Note** Not explicitly closing or disposing connections hurts performance.| + +These counters can be used with .NET Core global CLI tools: `dotnet-counters` and `dotnet-trace` in Windows or Linux and PerfView in Windows, using `Microsoft.Data.SqlClient.EventSource` as the provider name. For more information, see [Retrieve event counter values](https://docs.microsoft.com/en-us/sql/connect/ado-net/event-counters#retrieve-event-counter-values). + +```cmd +dotnet-counters monitor Microsoft.Data.SqlClient.EventSource -p +PerfView /onlyProviders=*Microsoft.Data.SqlClient.EventSource:EventCounterIntervalSec=1 collect +``` + +### Azure Identity dependency introduction +**Microsoft.Data.SqlClient** now depends on the **Azure.Identity** library to acquire tokens for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. This change brings the following changes to the public surface area: + +- **Breaking Change** + The "User Id" connection property now requires "Client Id" instead of "Object Id" for "User-Assigned Managed Identity". +- **Public API** + New read-only public property: `SqlAuthenticationParameters.ConnectionTimeout` +- **Dependency** + Azure.Identity v1.3.0 + +### Event tracing improvements in SNI.dll +`Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) versions have been updated to `v3.0.0-preview1.21104.2`. Event tracing in SNI.dll will no longer be enabled through a client application. Subscribing a session to the **Microsoft.Data.SqlClient.EventSource** provider through tools like xperf or perfview will be sufficient. For more information, see [Event tracing support in Native SNI](https://docs.microsoft.com/en-us/sql/connect/ado-net/enable-eventsource-tracing#event-tracing-support-in-native-sni). + +### Enabling row version null behavior +`SqlDataReader` returns a `DBNull` value instead of an empty `byte[]`. To enable the legacy behavior, you must enable the following AppContext switch on application startup: +**"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"** + +### Active Directory Default authentication support + +This PR introduces a new SQL Authentication method, **Active Directory Default**. This authentication mode widens the possibilities of user authentication, extending login solutions to the client environment, Visual Studio Code, Visual Studio, Azure CLI etc. + +With this authentication mode, the driver acquires a token by passing "[DefaultAzureCredential](https://docs.microsoft.com/dotnet/api/azure.identity.defaultazurecredential)" from the Azure Identity library to acquire an access token. This mode attempts to use these credential types to acquire an access token in the following order: + +- **EnvironmentCredential** + - Enables authentication to Azure Active Directory using client and secret, or username and password, details configured in the following environment variables: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_CLIENT_CERTIFICATE_PATH, AZURE_USERNAME, AZURE_PASSWORD ([More details](https://docs.microsoft.com/dotnet/api/azure.identity.environmentcredential)) +- **ManagedIdentityCredential** + - Attempts authentication to Azure Active Directory using a managed identity that has been assigned to the deployment environment. **"Client Id" of "User Assigned Managed Identity"** is read from the **"User Id" connection property**. +- **SharedTokenCacheCredential** + - Authenticates using tokens in the local cache shared between Microsoft applications. +- **VisualStudioCredential** + - Enables authentication to Azure Active Directory using data from Visual Studio +- **VisualStudioCodeCredential** + - Enables authentication to Azure Active Directory using data from Visual Studio Code. +- **AzureCliCredential** + - Enables authentication to Azure Active Directory using Azure CLI to obtain an access token. + +> InteractiveBrowserCredential is disabled in the driver implementation of "Active Directory Default", and "Active Directory Interactive" is the only option available to acquire a token using MFA/Interactive authentication.* + +> Further customization options are not available at the moment. + +### Custom master key store provider registration enhancements + +Microsoft.Data.SqlClient now offers more control of where master key store providers are accessible in an application in order to better support multi-tenant applications and their use of column encryption/decryption. The following APIs are introduced to allow registration of custom master key store providers on instances of `SqlConnection` and `SqlCommand`: + +```cs +public class SqlConnection +{ + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) +} +public class SqlCommand +{ + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(IDictionary customProviders) +} +``` + +The static API on `SqlConnection`, i.e. `SqlConnection.RegisterColumnEncryptionKeyStoreProviders` to register custom master key store providers globally continues to be supported. The column encryption key cache maintained globally only applies to globally registered providers. + +#### Column master key store provider registration precedence + +The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. No providers should be registered on the connection or command instances if one of the built-in column master key store providers is needed. + +Custom master key store providers can be registered with the driver at three different layers. The global level is as it currently is. The new per-connection and per-command level registrations will be empty initially and can be set more than once. + +The precedence of the three registrations are as follows: + +- The per-command registration will be checked if it is not empty. +- If the per-command registration is empty, the per-connection registration will be checked if it is not empty. +- If the per-connection registration is empty, the global registration will be checked. + +Once any key store provider is found at a registration level, the driver will **NOT** fall back to the other registrations to search for a provider. If providers are registered but the proper provider is not found at a level, an exception will be thrown containing only the registered providers in the registration that was checked. + +#### Column encryption key cache precedence + +The column encryption keys (CEKs) for custom key store providers registered using the new instance-level APIs will not be cached by the driver. The key store providers need to implement their own cache to gain performance. This local cache of column encryption keys implemented by custom key store providers will be disabled by the driver if the key store provider instance is registered in the driver at the global level. + +A new API has also been introduced on the `SqlColumnEncryptionKeyStoreProvider` base class to set the cache time to live: + +```cs +public abstract class SqlColumnEncryptionKeyStoreProvider +{ + // The default value of Column Encryption Key Cache Time to Live is 0. + // Provider's local cache is disabled for globally registered providers. + // Custom key store provider implementation must include column encryption key cache to provide caching support to locally registered providers. + public virtual TimeSpan? ColumnEncryptionKeyCacheTtl { get; set; } = new TimeSpan(0); +} +``` + +### IP Address preference + +A new connection property `IPAddressPreference` is introduced to specify the IP address family preference to the driver when establishing TCP connections. If `Transparent Network IP Resolution` (in .NET Framework) or `Multi Subnet Failover` is set to `true`, this setting has no effect. Below are the three accepted values for this property: + +- **IPv4First** + - This is the default preference value. The driver will use resolved IPv4 addresses first. If none of them can be connected to successfully, it will try resolved IPv6 addresses. + +- **IPv6First** + - The driver will use resolved IPv6 addresses first. If none of them can be connected to successfully, it will try resolved IPv4 addresses. + +- **UsePlatformDefault** + - The driver will try IP addresses in the order received from the DNS resolution response. + + +## Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 3.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Loader 4.3.0 diff --git a/release-notes/3.0/3.0.md b/release-notes/3.0/3.0.md index 48948b83b5..94dc6d0b24 100644 --- a/release-notes/3.0/3.0.md +++ b/release-notes/3.0/3.0.md @@ -1,5 +1,11 @@ # Microsoft.Data.SqlClient 3.0 Releases +The following Microsoft.Data.SqlClient 3.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2020/06/09 | 3.0.0 | [release notes](3.0.0.md) | + The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: | Release Date | Version | Notes | diff --git a/release-notes/3.0/README.md b/release-notes/3.0/README.md index 48948b83b5..94dc6d0b24 100644 --- a/release-notes/3.0/README.md +++ b/release-notes/3.0/README.md @@ -1,5 +1,11 @@ # Microsoft.Data.SqlClient 3.0 Releases +The following Microsoft.Data.SqlClient 3.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2020/06/09 | 3.0.0 | [release notes](3.0.0.md) | + The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: | Release Date | Version | Notes | diff --git a/release-notes/README.md b/release-notes/README.md index b59175696b..0bd2dfa84b 100644 --- a/release-notes/README.md +++ b/release-notes/README.md @@ -1,6 +1,6 @@ # Microsoft.Data.SqlClient Release Notes -The latest stable release is [Microsoft.Data.SqlClient 2.1](2.1). +The latest stable release is [Microsoft.Data.SqlClient 3.0](3.0). ## Release Information From 1e8ce9766c79b12f7835a8679058d9bce41f7dbc Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 9 Jun 2021 23:41:01 -0700 Subject: [PATCH 66/87] Minor update --- release-notes/3.0/3.0.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-notes/3.0/3.0.0.md b/release-notes/3.0/3.0.0.md index 8b91f2742b..6bef9eda1a 100644 --- a/release-notes/3.0/3.0.0.md +++ b/release-notes/3.0/3.0.0.md @@ -279,7 +279,7 @@ A new connection property `IPAddressPreference` is introduced to specify the IP ## Target Platform Support -- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Framework 4.6.1+ (Windows x86, Windows x64) - .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) - .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) From f17bba2f5cfdff0e64e7cb94a5a7f2eb28194a6b Mon Sep 17 00:00:00 2001 From: Wraith Date: Fri, 11 Jun 2021 22:03:26 +0100 Subject: [PATCH 67/87] Perf: Static delegates (#1060) * netcore static lambda rework * minor netcore api cleanup * netfx static lambda rework * netcore add strongbox to avoid boxing * fixup 2 anonymous typed lambdas to be explicit --- .../Data/SqlClient/SNI/SNILoadHandle.cs | 2 +- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 183 +++++++++------- .../Microsoft/Data/SqlClient/SqlCommand.cs | 189 +++++++++-------- .../Microsoft/Data/SqlClient/SqlConnection.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 69 +++--- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 4 +- .../Data/SqlClient/TdsParserStateObject.cs | 11 +- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 200 +++++++++--------- .../Microsoft/Data/SqlClient/SqlCommand.cs | 76 +++---- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 4 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 152 ++++++++++++- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 27 ++- .../Data/SqlClient/TdsParserStateObject.cs | 5 +- 13 files changed, 561 insertions(+), 363 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs index 1fa6c58a3f..5f932703af 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs @@ -14,7 +14,7 @@ internal class SNILoadHandle public static readonly SNILoadHandle SingletonInstance = new SNILoadHandle(); public readonly EncryptionOptions _encryptionOption = EncryptionOptions.OFF; - public ThreadLocal _lastError = new ThreadLocal(() => { return new SNIError(SNIProviders.INVALID_PROV, 0, TdsEnums.SNI_SUCCESS, string.Empty); }); + public ThreadLocal _lastError = new ThreadLocal(static () => new SNIError(SNIProviders.INVALID_PROV, 0, TdsEnums.SNI_SUCCESS, string.Empty)); private readonly uint _status = TdsEnums.SNI_SUCCESS; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index b0dce3a6f9..8d01563b37 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -9,6 +9,7 @@ using System.Data.Common; using System.Data.SqlTypes; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -1078,14 +1079,19 @@ private Task ReadFromRowSourceAsync(CancellationToken cts) if (_isAsyncBulkCopy && _dbDataReaderRowSource != null) { // This will call ReadAsync for DbDataReader (for SqlDataReader it will be truly async read; for non-SqlDataReader it may block.) - return _dbDataReaderRowSource.ReadAsync(cts).ContinueWith((t) => - { - if (t.Status == TaskStatus.RanToCompletion) + return _dbDataReaderRowSource.ReadAsync(cts).ContinueWith( + static (Task task, object state) => { - _hasMoreRowToCopy = t.Result; - } - return t; - }, TaskScheduler.Default).Unwrap(); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + if (task.Status == TaskStatus.RanToCompletion) + { + sqlBulkCopy._hasMoreRowToCopy = task.Result; + } + return task; + }, + state: this, + scheduler: TaskScheduler.Default + ).Unwrap(); } else { // This will call Read for DataRows, DataTable and IDataReader (this includes all IDataReader except DbDataReader) @@ -1940,7 +1946,7 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok { AsyncHelper.ContinueTaskWithState(writeTask, tcs, state: tcs, - onSuccess: state => ((TaskCompletionSource)state).SetResult(null) + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); } }, ctoken); // We do not need to propagate exception, etc, from reconnect task, we just need to wait for it to finish. @@ -1948,7 +1954,7 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok } else { - AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, () => { throw SQL.CR_ReconnectTimeout(); }, rethrowExceptions: false); + AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, static () => throw SQL.CR_ReconnectTimeout(), rethrowExceptions: false); } } @@ -1969,27 +1975,32 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok if (resultTask != null) { finishedSynchronously = false; - return resultTask.ContinueWith((t) => - { - try + return resultTask.ContinueWith( + static (Task t, object state) => { - AbortTransaction(); // If there is one, on success transactions will be committed. - } - finally - { - _isBulkCopyingInProgress = false; - if (_parser != null) + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + try { - _parser._asyncWrite = false; + sqlBulkCopy.AbortTransaction(); // If there is one, on success transactions will be committed. } - if (_parserLock != null) + finally { - _parserLock.Release(); - _parserLock = null; + sqlBulkCopy._isBulkCopyingInProgress = false; + if (sqlBulkCopy._parser != null) + { + sqlBulkCopy._parser._asyncWrite = false; + } + if (sqlBulkCopy._parserLock != null) + { + sqlBulkCopy._parserLock.Release(); + sqlBulkCopy._parserLock = null; + } } - } - return t; - }, TaskScheduler.Default).Unwrap(); + return t; + }, + state: this, + scheduler: TaskScheduler.Default + ).Unwrap(); } return null; } @@ -2254,12 +2265,13 @@ private Task CopyColumnsAsync(int col, TaskCompletionSource source = nul // This is in its own method to avoid always allocating the lambda in CopyColumnsAsync private void CopyColumnsAsyncSetupContinuation(TaskCompletionSource source, Task task, int i) { - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - if (i + 1 < _sortedColumnMappings.Count) + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + if (i + 1 < sqlBulkCopy._sortedColumnMappings.Count) { - CopyColumnsAsync(i + 1, source); //continue from the next column + sqlBulkCopy.CopyColumnsAsync(i + 1, source); //continue from the next column } else { @@ -2401,8 +2413,9 @@ private Task CopyRowsAsync(int rowsSoFar, int totalRows, CancellationToken cts, } resultTask = source.Task; - AsyncHelper.ContinueTask(readTask, source, - onSuccess: () => CopyRowsAsync(i + 1, totalRows, cts, source) + AsyncHelper.ContinueTaskWithState(readTask, source, this, + onSuccess: (object state) => ((SqlBulkCopy)state).CopyRowsAsync(i + 1, totalRows, cts, source) + ); return resultTask; // Associated task will be completed when all rows are copied to server/exception/cancelled. } @@ -2412,20 +2425,21 @@ private Task CopyRowsAsync(int rowsSoFar, int totalRows, CancellationToken cts, source = source ?? new TaskCompletionSource(); resultTask = source.Task; - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - CheckAndRaiseNotification(); // Check for notification now as the current row copy is done at this moment. + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + sqlBulkCopy.CheckAndRaiseNotification(); // Check for notification now as the current row copy is done at this moment. - Task readTask = ReadFromRowSourceAsync(cts); + Task readTask = sqlBulkCopy.ReadFromRowSourceAsync(cts); if (readTask == null) { - CopyRowsAsync(i + 1, totalRows, cts, source); + sqlBulkCopy.CopyRowsAsync(i + 1, totalRows, cts, source); } else { - AsyncHelper.ContinueTask(readTask, source, - onSuccess: () => CopyRowsAsync(i + 1, totalRows, cts, source) + AsyncHelper.ContinueTaskWithState(readTask, source, sqlBulkCopy, + onSuccess: (object state2) => ((SqlBulkCopy)state2).CopyRowsAsync(i + 1, totalRows, cts, source) ); } } @@ -2498,14 +2512,15 @@ private Task CopyBatchesAsync(BulkCopySimpleResultSet internalResults, string up source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(commandTask, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(commandTask, source, this, + onSuccess: (object state) => { - Task continuedTask = CopyBatchesAsyncContinued(internalResults, updateBulkCommandText, cts, source); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + Task continuedTask = sqlBulkCopy.CopyBatchesAsyncContinued(internalResults, updateBulkCommandText, cts, source); if (continuedTask == null) { // Continuation finished sync, recall into CopyBatchesAsync to continue - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); } } ); @@ -2562,18 +2577,19 @@ private Task CopyBatchesAsyncContinued(BulkCopySimpleResultSet internalResults, { // First time only source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - Task continuedTask = CopyBatchesAsyncContinuedOnSuccess(internalResults, updateBulkCommandText, cts, source); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + Task continuedTask = sqlBulkCopy.CopyBatchesAsyncContinuedOnSuccess(internalResults, updateBulkCommandText, cts, source); if (continuedTask == null) { // Continuation finished sync, recall into CopyBatchesAsync to continue - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); } }, - onFailure: _ => CopyBatchesAsyncContinuedOnError(cleanupParser: false), - onCancellation: () => CopyBatchesAsyncContinuedOnError(cleanupParser: true) + onFailure: static (Exception _, object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: false), + onCancellation: static (object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: true) ); return source.Task; @@ -2621,24 +2637,25 @@ private Task CopyBatchesAsyncContinuedOnSuccess(BulkCopySimpleResultSet internal source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(writeTask, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(writeTask, source, this, + onSuccess: (object state) => { + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; try { - RunParser(); - CommitTransaction(); + sqlBulkCopy.RunParser(); + sqlBulkCopy.CommitTransaction(); } catch (Exception) { - CopyBatchesAsyncContinuedOnError(cleanupParser: false); + sqlBulkCopy.CopyBatchesAsyncContinuedOnError(cleanupParser: false); throw; } // Always call back into CopyBatchesAsync - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); }, - onFailure: _ => CopyBatchesAsyncContinuedOnError(cleanupParser: false) + onFailure: static (Exception _, object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: false) ); return source.Task; } @@ -2758,16 +2775,17 @@ private void WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet int { source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; // Bulk copy task is completed at this moment. if (task.IsCanceled) { - _localColumnMappings = null; + sqlBulkCopy._localColumnMappings = null; try { - CleanUpStateObject(); + sqlBulkCopy.CleanUpStateObject(); } finally { @@ -2780,10 +2798,10 @@ private void WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet int } else { - _localColumnMappings = null; + sqlBulkCopy._localColumnMappings = null; try { - CleanUpStateObject(isCancelRequested: false); + sqlBulkCopy.CleanUpStateObject(isCancelRequested: false); } finally { @@ -2885,23 +2903,31 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio { if (_isAsyncBulkCopy) { - CancellationTokenRegistration regReconnectCancel = new CancellationTokenRegistration(); + StrongBox regReconnectCancel = new StrongBox(new CancellationTokenRegistration()); TaskCompletionSource cancellableReconnectTS = new TaskCompletionSource(); if (cts.CanBeCanceled) { - regReconnectCancel = cts.Register(s => ((TaskCompletionSource)s).TrySetCanceled(), cancellableReconnectTS); + regReconnectCancel.Value = cts.Register(static (object tcs) => ((TaskCompletionSource)tcs).TrySetCanceled(), cancellableReconnectTS); } AsyncHelper.ContinueTaskWithState(reconnectTask, cancellableReconnectTS, state: cancellableReconnectTS, - onSuccess: (state) => { ((TaskCompletionSource)state).SetResult(null); } + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); // No need to cancel timer since SqlBulkCopy creates specific task source for reconnection. - AsyncHelper.SetTimeoutException(cancellableReconnectTS, BulkCopyTimeout, - () => { return SQL.BulkLoadInvalidDestinationTable(_destinationTableName, SQL.CR_ReconnectTimeout()); }, CancellationToken.None); - AsyncHelper.ContinueTask(cancellableReconnectTS.Task, source, - onSuccess: () => + AsyncHelper.SetTimeoutExceptionWithState( + completion: cancellableReconnectTS, + timeout: BulkCopyTimeout, + state: _destinationTableName, + onFailure: static (object state) => SQL.BulkLoadInvalidDestinationTable((string)state, SQL.CR_ReconnectTimeout()), + cancellationToken: CancellationToken.None + ); + AsyncHelper.ContinueTaskWithState( + task: cancellableReconnectTS.Task, + completion: source, + state: regReconnectCancel, + onSuccess: (object state) => { - regReconnectCancel.Dispose(); + ((StrongBox)state).Value.Dispose(); if (_parserLock != null) { _parserLock.Release(); @@ -2911,8 +2937,8 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio _parserLock.Wait(canReleaseFromAnyThread: true); WriteToServerInternalRestAsync(cts, source); }, - onFailure: (e) => { regReconnectCancel.Dispose(); }, - onCancellation: () => { regReconnectCancel.Dispose(); }, + onFailure: static (Exception _, object state) => ((StrongBox)state).Value.Dispose(), + onCancellation: static (object state) => ((StrongBox)state).Value.Dispose(), exceptionConverter: (ex) => SQL.BulkLoadInvalidDestinationTable(_destinationTableName, ex)); return; } @@ -2920,7 +2946,7 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio { try { - AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, static () => throw SQL.CR_ReconnectTimeout()); } catch (SqlException ex) { @@ -2961,8 +2987,8 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio if (internalResultsTask != null) { - AsyncHelper.ContinueTask(internalResultsTask, source, - onSuccess: () => WriteToServerInternalRestContinuedAsync(internalResultsTask.Result, cts, source) + AsyncHelper.ContinueTaskWithState(internalResultsTask, source, this, + onSuccess: (object state) => ((SqlBulkCopy)state).WriteToServerInternalRestContinuedAsync(internalResultsTask.Result, cts, source) ); } else @@ -3034,16 +3060,17 @@ private Task WriteToServerInternalAsync(CancellationToken ctoken) else { Debug.Assert(_isAsyncBulkCopy, "Read must not return a Task in the Sync mode"); - AsyncHelper.ContinueTask(readTask, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(readTask, source, this, + onSuccess: (object state) => { - if (!_hasMoreRowToCopy) + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + if (!sqlBulkCopy._hasMoreRowToCopy) { source.SetResult(null); // No rows to copy! } else { - WriteToServerInternalRestAsync(ctoken, source); // Passing the same completion which will be completed by the Callee. + sqlBulkCopy.WriteToServerInternalRestAsync(ctoken, source); // Passing the same completion which will be completed by the Callee. } } ); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 24a82db701..238e5d7221 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -1249,7 +1249,7 @@ private IAsyncResult BeginExecuteNonQueryInternal(CommandBehavior behavior, Asyn if (callback != null) { globalCompletion.Task.ContinueWith( - (task, state) => ((AsyncCallback)state)(task), + static (task, state) => ((AsyncCallback)state)(task), state: callback ); } @@ -1758,7 +1758,7 @@ private IAsyncResult BeginExecuteXmlReaderInternal(CommandBehavior behavior, Asy if (callback != null) { localCompletion.Task.ContinueWith( - (task, state) => ((AsyncCallback)state)(task), + static (task, state) => ((AsyncCallback)state)(task), state: callback ); } @@ -2189,7 +2189,7 @@ private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncC if (callback != null) { globalCompletion.Task.ContinueWith( - (task, state) => ((AsyncCallback)state)(task), + static (task, state) => ((AsyncCallback)state)(task), state: callback ); } @@ -2510,14 +2510,19 @@ private Task InternalExecuteNonQueryAsync(CancellationToken cancellationTok /// protected override Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { - return ExecuteReaderAsync(behavior, cancellationToken).ContinueWith((result) => - { - if (result.IsFaulted) + return ExecuteReaderAsync(behavior, cancellationToken).ContinueWith( + static (Task result) => { - throw result.Exception.InnerException; - } - return result.Result; - }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); + if (result.IsFaulted) + { + throw result.Exception.InnerException; + } + return result.Result; + }, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, + TaskScheduler.Default + ); } /// @@ -3323,7 +3328,7 @@ private Task RunExecuteNonQueryTds(string methodName, bool isAsync, int timeout, } else { - AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, timeout, static () => throw SQL.CR_ReconnectTimeout()); timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart); } } @@ -3380,7 +3385,7 @@ private Task RunExecuteNonQueryTds(string methodName, bool isAsync, int timeout, private void RunExecuteNonQueryTdsSetupReconnnectContinuation(string methodName, bool isAsync, int timeout, bool asyncWrite, Task reconnectTask, long reconnectionStart, TaskCompletionSource completion) { CancellationTokenSource timeoutCTS = new CancellationTokenSource(); - AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token); + AsyncHelper.SetTimeoutException(completion, timeout, static () => SQL.CR_ReconnectTimeout(), timeoutCTS.Token); AsyncHelper.ContinueTask(reconnectTask, completion, () => { @@ -3399,7 +3404,7 @@ private void RunExecuteNonQueryTdsSetupReconnnectContinuation(string methodName, { AsyncHelper.ContinueTaskWithState(subTask, completion, state: completion, - onSuccess: (state) => ((TaskCompletionSource)state).SetResult(null) + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); } } @@ -3642,70 +3647,72 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r SqlDataReader describeParameterEncryptionDataReader, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, bool describeParameterEncryptionNeeded) { - returnTask = AsyncHelper.CreateContinuationTask(fetchInputParameterEncryptionInfoTask, () => - { - bool processFinallyBlockAsync = true; - bool decrementAsyncCountInFinallyBlockAsync = true; - - RuntimeHelpers.PrepareConstrainedRegions(); - try + returnTask = AsyncHelper.CreateContinuationTaskWithState(fetchInputParameterEncryptionInfoTask, this, + (object state) => { - // Check for any exceptions on network write, before reading. - CheckThrowSNIException(); + SqlCommand command = (SqlCommand)state; + bool processFinallyBlockAsync = true; + bool decrementAsyncCountInFinallyBlockAsync = true; - // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. - // Decrement it when we are about to complete async execute reader. - SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection(); - if (internalConnectionTds != null) + RuntimeHelpers.PrepareConstrainedRegions(); + try { - internalConnectionTds.DecrementAsyncCount(); - decrementAsyncCountInFinallyBlockAsync = false; - } + // Check for any exceptions on network write, before reading. + command.CheckThrowSNIException(); - // Complete executereader. - describeParameterEncryptionDataReader = - CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); - Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); + // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. + // Decrement it when we are about to complete async execute reader. + SqlInternalConnectionTds internalConnectionTds = command._activeConnection.GetOpenTdsConnection(); + if (internalConnectionTds != null) + { + internalConnectionTds.DecrementAsyncCount(); + decrementAsyncCountInFinallyBlockAsync = false; + } - // Read the results of describe parameter encryption. - ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, - describeParameterEncryptionRpcOriginalRpcMap); + // Complete executereader. + describeParameterEncryptionDataReader = command.CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); + Debug.Assert(null == command._stateObj, "non-null state object in PrepareForTransparentEncryption."); -#if DEBUG - // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. - if (_sleepAfterReadDescribeEncryptionParameterResults) + // Read the results of describe parameter encryption. + command.ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); + + #if DEBUG + // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. + if (_sleepAfterReadDescribeEncryptionParameterResults) + { + Thread.Sleep(10000); + } + #endif + } + catch (Exception e) { - Thread.Sleep(10000); + processFinallyBlockAsync = ADP.IsCatchableExceptionType(e); + throw; } -#endif - } - catch (Exception e) - { - processFinallyBlockAsync = ADP.IsCatchableExceptionType(e); - throw; - } - finally - { - PrepareTransparentEncryptionFinallyBlock(closeDataReader: processFinallyBlockAsync, - decrementAsyncCount: decrementAsyncCountInFinallyBlockAsync, - clearDataStructures: processFinallyBlockAsync, - wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded, - describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap, - describeParameterEncryptionDataReader: describeParameterEncryptionDataReader); - } - }, - onFailure: ((exception) => - { - if (_cachedAsyncState != null) + finally + { + command.PrepareTransparentEncryptionFinallyBlock(closeDataReader: processFinallyBlockAsync, + decrementAsyncCount: decrementAsyncCountInFinallyBlockAsync, + clearDataStructures: processFinallyBlockAsync, + wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded, + describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap, + describeParameterEncryptionDataReader: describeParameterEncryptionDataReader); + } + }, + onFailure: static (Exception exception, object state) => { - _cachedAsyncState.ResetAsyncState(); - } + SqlCommand command = (SqlCommand)state; + if (command._cachedAsyncState != null) + { + command._cachedAsyncState.ResetAsyncState(); + } - if (exception != null) - { - throw exception; + if (exception != null) + { + throw exception; + } } - })); + ); return describeParameterEncryptionDataReader; } @@ -4517,40 +4524,35 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior { long parameterEncryptionStart = ADP.TimerCurrent(); TaskCompletionSource completion = new TaskCompletionSource(); - AsyncHelper.ContinueTask(describeParameterEncryptionTask, completion, - () => + AsyncHelper.ContinueTaskWithState(describeParameterEncryptionTask, completion, this, + (object state) => { + SqlCommand command = (SqlCommand)state; Task subTask = null; - GenerateEnclavePackage(); - RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, isAsync, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds); + command.GenerateEnclavePackage(); + command.RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, isAsync, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds); if (subTask == null) { completion.SetResult(null); } else { - AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null)); + AsyncHelper.ContinueTaskWithState(subTask, completion, completion, static (object state) => ((TaskCompletionSource)state).SetResult(null)); } }, - onFailure: ((exception) => + onFailure: static (Exception exception, object state) => { - if (_cachedAsyncState != null) - { - _cachedAsyncState.ResetAsyncState(); - } + ((SqlCommand)state)._cachedAsyncState?.ResetAsyncState(); if (exception != null) { throw exception; } - }), - onCancellation: (() => + }, + onCancellation: static (object state) => { - if (_cachedAsyncState != null) - { - _cachedAsyncState.ResetAsyncState(); - } - }) - ); + ((SqlCommand)state)._cachedAsyncState?.ResetAsyncState(); + } + ); task = completion.Task; return ds; } @@ -4623,7 +4625,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi } else { - AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, timeout, static () => throw SQL.CR_ReconnectTimeout()); timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart); } } @@ -4837,15 +4839,18 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi private Task RunExecuteReaderTdsSetupContinuation(RunBehavior runBehavior, SqlDataReader ds, string optionSettings, Task writeTask) { - Task task = AsyncHelper.CreateContinuationTask(writeTask, - onSuccess: () => + Task task = AsyncHelper.CreateContinuationTaskWithState( + task: writeTask, + state: _activeConnection, + onSuccess: (object state) => { - _activeConnection.GetOpenTdsConnection(); // it will throw if connection is closed + SqlConnection sqlConnection = (SqlConnection)state; + sqlConnection.GetOpenTdsConnection(); // it will throw if connection is closed cachedAsyncState.SetAsyncReaderState(ds, runBehavior, optionSettings); }, - onFailure: (exc) => + onFailure: static (Exception exc, object state) => { - _activeConnection.GetOpenTdsConnection().DecrementAsyncCount(); + ((SqlConnection)state).GetOpenTdsConnection().DecrementAsyncCount(); } ); return task; @@ -4855,7 +4860,7 @@ private Task RunExecuteReaderTdsSetupContinuation(RunBehavior runBehavior, SqlDa private void RunExecuteReaderTdsSetupReconnectContinuation(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, bool isAsync, int timeout, bool asyncWrite, bool inRetry, SqlDataReader ds, Task reconnectTask, long reconnectionStart, TaskCompletionSource completion) { CancellationTokenSource timeoutCTS = new CancellationTokenSource(); - AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token); + AsyncHelper.SetTimeoutException(completion, timeout, static () => SQL.CR_ReconnectTimeout(), timeoutCTS.Token); AsyncHelper.ContinueTask(reconnectTask, completion, () => { @@ -4875,7 +4880,7 @@ private void RunExecuteReaderTdsSetupReconnectContinuation(CommandBehavior cmdBe { AsyncHelper.ContinueTaskWithState(subTask, completion, state: completion, - onSuccess: (state) => ((TaskCompletionSource)state).SetResult(null) + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 09b62042f8..4879a3111c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -2152,7 +2152,7 @@ internal Task RegisterForConnectionCloseNotification(Task outerTask, ob { // Connection exists, schedule removal, will be added to ref collection after calling ValidateAndReconnect return outerTask.ContinueWith( - continuationFunction: (task, state) => + continuationFunction: static (task, state) => { Tuple parameters = (Tuple)state; SqlConnection connection = parameters.Item1; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 0ede264635..46c874ab80 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -34,7 +34,7 @@ internal static Task CreateContinuationTask(Task task, Action onSuccess, Action< TaskCompletionSource completion = new TaskCompletionSource(); ContinueTaskWithState(task, completion, state: Tuple.Create(onSuccess, onFailure, completion), - onSuccess: (state) => + onSuccess: static (object state) => { var parameters = (Tuple, TaskCompletionSource>)state; Action success = parameters.Item1; @@ -42,7 +42,7 @@ internal static Task CreateContinuationTask(Task task, Action onSuccess, Action< success(); taskCompletionSource.SetResult(null); }, - onFailure: (exception, state) => + onFailure: static (Exception exception, object state) => { var parameters = (Tuple, TaskCompletionSource>)state; Action failure = parameters.Item2; @@ -64,7 +64,7 @@ internal static Task CreateContinuationTaskWithState(Task task, object state, Ac { var completion = new TaskCompletionSource(); ContinueTaskWithState(task, completion, state, - onSuccess: (continueState) => + onSuccess: (object continueState) => { onSuccess(continueState); completion.SetResult(null); @@ -81,12 +81,12 @@ internal static Task CreateContinuationTaskWithState(Task task, object state, Ac } internal static void ContinueTask(Task task, - TaskCompletionSource completion, - Action onSuccess, - Action onFailure = null, - Action onCancellation = null, - Func exceptionConverter = null - ) + TaskCompletionSource completion, + Action onSuccess, + Action onFailure = null, + Action onCancellation = null, + Func exceptionConverter = null + ) { task.ContinueWith( tsk => @@ -145,7 +145,7 @@ internal static Task CreateContinuationTaskWithState(Task task, object state, Ac ) { task.ContinueWith( - tsk => + (Task tsk, object state2) => { if (tsk.Exception != null) { @@ -156,7 +156,7 @@ internal static Task CreateContinuationTaskWithState(Task task, object state, Ac } try { - onFailure?.Invoke(exc, state); + onFailure?.Invoke(exc, state2); } finally { @@ -167,7 +167,7 @@ internal static Task CreateContinuationTaskWithState(Task task, object state, Ac { try { - onCancellation?.Invoke(state); + onCancellation?.Invoke(state2); } finally { @@ -178,14 +178,16 @@ internal static Task CreateContinuationTaskWithState(Task task, object state, Ac { try { - onSuccess(state); + onSuccess(state2); } catch (Exception e) { completion.SetException(e); } } - }, TaskScheduler.Default + }, + state: state, + scheduler: TaskScheduler.Default ); } @@ -205,25 +207,42 @@ internal static void WaitForCompletion(Task task, int timeout, Action onTimeout } if (!task.IsCompleted) { - task.ContinueWith(t => { var ignored = t.Exception; }); //Ensure the task does not leave an unobserved exception - if (onTimeout != null) - { - onTimeout(); - } + task.ContinueWith(static t => { var ignored = t.Exception; }); //Ensure the task does not leave an unobserved exception + onTimeout?.Invoke(); } } - internal static void SetTimeoutException(TaskCompletionSource completion, int timeout, Func exc, CancellationToken ctoken) + internal static void SetTimeoutException(TaskCompletionSource completion, int timeout, Func onFailure, CancellationToken ctoken) { if (timeout > 0) { - Task.Delay(timeout * 1000, ctoken).ContinueWith((tsk) => - { - if (!tsk.IsCanceled && !completion.Task.IsCompleted) + Task.Delay(timeout * 1000, ctoken).ContinueWith( + (Task task) => { - completion.TrySetException(exc()); + if (!task.IsCanceled && !completion.Task.IsCompleted) + { + completion.TrySetException(onFailure()); + } } - }); + ); + } + } + + internal static void SetTimeoutExceptionWithState(TaskCompletionSource completion, int timeout, object state, Func onFailure, CancellationToken cancellationToken) + { + if (timeout > 0) + { + Task.Delay(timeout * 1000, cancellationToken).ContinueWith( + (Task task, object state) => + { + if (!task.IsCanceled && !completion.Task.IsCompleted) + { + completion.TrySetException(onFailure(state)); + } + }, + state: state, + cancellationToken: CancellationToken.None + ); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 04ec52be41..204b736715 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -8842,7 +8842,7 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques bool taskReleaseConnectionLock = releaseConnectionLock; releaseConnectionLock = false; return executeTask.ContinueWith( - (task, state) => + static (Task task, object state) => { Debug.Assert(!task.IsCanceled, "Task should not be canceled"); var parameters = (Tuple)state; @@ -9068,7 +9068,7 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques if (releaseConnectionLock) { task.ContinueWith( - (_, state) => ((SqlInternalConnectionTds)state)._parserLock.Release(), + static (Task _, object state) => ((SqlInternalConnectionTds)state)._parserLock.Release(), state: _connHandler, TaskScheduler.Default ); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 05bba67a5a..8645cee07e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1016,7 +1016,16 @@ internal Task ExecuteFlush() } else { - return AsyncHelper.CreateContinuationTask(writePacketTask, () => { HasPendingData = true; _messageStatus = 0; }); + return AsyncHelper.CreateContinuationTaskWithState( + task: writePacketTask, + state: this, + onSuccess: static (object state) => + { + TdsParserStateObject stateObject = (TdsParserStateObject)state; + stateObject.HasPendingData = true; + stateObject._messageStatus = 0; + } + ); } } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 19ad9e6b53..3a2f91e7e7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -1118,14 +1118,18 @@ private Task ReadFromRowSourceAsync(CancellationToken cts) if (_isAsyncBulkCopy && _dbDataReaderRowSource != null) { // This will call ReadAsync for DbDataReader (for SqlDataReader it will be truly async read; for non-SqlDataReader it may block.) - return _dbDataReaderRowSource.ReadAsync(cts).ContinueWith((t) => - { - if (t.Status == TaskStatus.RanToCompletion) + return _dbDataReaderRowSource.ReadAsync(cts).ContinueWith( + static (Task task, object state) => { - _hasMoreRowToCopy = t.Result; - } - return t; - }, TaskScheduler.Default).Unwrap(); + if (task.Status == TaskStatus.RanToCompletion) + { + ((SqlBulkCopy)state)._hasMoreRowToCopy = task.Result; + } + return task; + }, + state: this, + scheduler: TaskScheduler.Default + ).Unwrap(); } else { // This will call Read for DataRows, DataTable and IDataReader (this includes all IDataReader except DbDataReader) @@ -2023,8 +2027,8 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok } else { - AsyncHelper.ContinueTask(writeTask, tcs, - onSuccess: () => tcs.SetResult(null) + AsyncHelper.ContinueTaskWithState(writeTask, tcs, tcs, + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); } }, ctoken); // We do not need to propagate exception, etc, from reconnect task, we just need to wait for it to finish. @@ -2032,7 +2036,7 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok } else { - AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, () => { throw SQL.CR_ReconnectTimeout(); }, rethrowExceptions: false); + AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, static () => throw SQL.CR_ReconnectTimeout(), rethrowExceptions: false); } } @@ -2066,27 +2070,32 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok if (resultTask != null) { finishedSynchronously = false; - return resultTask.ContinueWith((t) => - { - try - { - AbortTransaction(); // if there is one, on success transactions will be commited - } - finally + return resultTask.ContinueWith( + static (Task task, object state) => { - _isBulkCopyingInProgress = false; - if (_parser != null) + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + try { - _parser._asyncWrite = false; + sqlBulkCopy.AbortTransaction(); // if there is one, on success transactions will be commited } - if (_parserLock != null) + finally { - _parserLock.Release(); - _parserLock = null; + sqlBulkCopy._isBulkCopyingInProgress = false; + if (sqlBulkCopy._parser != null) + { + sqlBulkCopy._parser._asyncWrite = false; + } + if (sqlBulkCopy._parserLock != null) + { + sqlBulkCopy._parserLock.Release(); + sqlBulkCopy._parserLock = null; + } } - } - return t; - }, TaskScheduler.Default).Unwrap(); + return task; + }, + state: this, + scheduler: TaskScheduler.Default + ).Unwrap(); } return null; } @@ -2360,12 +2369,13 @@ private Task CopyColumnsAsync(int col, TaskCompletionSource source = nul // This is in its own method to avoid always allocating the lambda in CopyColumnsAsync private void CopyColumnsAsyncSetupContinuation(TaskCompletionSource source, Task task, int i) { - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - if (i + 1 < _sortedColumnMappings.Count) + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + if (i + 1 < sqlBulkCopy._sortedColumnMappings.Count) { - CopyColumnsAsync(i + 1, source); //continue from the next column + sqlBulkCopy.CopyColumnsAsync(i + 1, source); //continue from the next column } else { @@ -2468,26 +2478,6 @@ private Task CheckForCancellation(CancellationToken cts, TaskCompletionSource ContinueTaskPend(Task task, TaskCompletionSource source, Func> action) - { - if (task == null) - { - return action(); - } - else - { - Debug.Assert(source != null, "source should already be initialized if task is not null"); - AsyncHelper.ContinueTask(task, source, - onSuccess: () => - { - TaskCompletionSource newSource = action(); - Debug.Assert(newSource == null, "Shouldn't create a new source when one already exists"); - } - ); - } - return null; - } - // Copies all the rows in a batch. // Maintains state machine with state variable: rowSoFar. // Returned Task could be null in two cases: (1) _isAsyncBulkCopy == false, or (2) _isAsyncBulkCopy == true but all async writes finished synchronously. @@ -2528,8 +2518,8 @@ private Task CopyRowsAsync(int rowsSoFar, int totalRows, CancellationToken cts, } resultTask = source.Task; - AsyncHelper.ContinueTask(readTask, source, - onSuccess: () => CopyRowsAsync(i + 1, totalRows, cts, source), + AsyncHelper.ContinueTaskWithState(readTask, source, this, + onSuccess: (object state) => ((SqlBulkCopy)state).CopyRowsAsync(i + 1, totalRows, cts, source), connectionToDoom: _connection.GetOpenTdsConnection() ); return resultTask; // Associated task will be completed when all rows are copied to server/exception/cancelled. @@ -2540,20 +2530,21 @@ private Task CopyRowsAsync(int rowsSoFar, int totalRows, CancellationToken cts, source = source ?? new TaskCompletionSource(); resultTask = source.Task; - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - CheckAndRaiseNotification(); // Check for notification now as the current row copy is done at this moment. + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + sqlBulkCopy.CheckAndRaiseNotification(); // Check for notification now as the current row copy is done at this moment. - Task readTask = ReadFromRowSourceAsync(cts); + Task readTask = sqlBulkCopy.ReadFromRowSourceAsync(cts); if (readTask == null) { - CopyRowsAsync(i + 1, totalRows, cts, source); + sqlBulkCopy.CopyRowsAsync(i + 1, totalRows, cts, source); } else { - AsyncHelper.ContinueTask(readTask, source, - onSuccess: () => CopyRowsAsync(i + 1, totalRows, cts, source), + AsyncHelper.ContinueTaskWithState(readTask, source, sqlBulkCopy, + onSuccess: (object state2) => ((SqlBulkCopy)state2).CopyRowsAsync(i + 1, totalRows, cts, source), connectionToDoom: _connection.GetOpenTdsConnection() ); } @@ -2628,14 +2619,15 @@ private Task CopyBatchesAsync(BulkCopySimpleResultSet internalResults, string up source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(commandTask, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(commandTask, source, this, + onSuccess: (object state) => { - Task continuedTask = CopyBatchesAsyncContinued(internalResults, updateBulkCommandText, cts, source); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + Task continuedTask = sqlBulkCopy.CopyBatchesAsyncContinued(internalResults, updateBulkCommandText, cts, source); if (continuedTask == null) { // Continuation finished sync, recall into CopyBatchesAsync to continue - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); } }, connectionToDoom: _connection.GetOpenTdsConnection() @@ -2693,20 +2685,21 @@ private Task CopyBatchesAsyncContinued(BulkCopySimpleResultSet internalResults, { // First time only source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - Task continuedTask = CopyBatchesAsyncContinuedOnSuccess(internalResults, updateBulkCommandText, cts, source); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + Task continuedTask = sqlBulkCopy.CopyBatchesAsyncContinuedOnSuccess(internalResults, updateBulkCommandText, cts, source); if (continuedTask == null) { // Continuation finished sync, recall into CopyBatchesAsync to continue - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); } }, - connectionToDoom: _connection.GetOpenTdsConnection(), - onFailure: _ => CopyBatchesAsyncContinuedOnError(cleanupParser: false), - onCancellation: () => CopyBatchesAsyncContinuedOnError(cleanupParser: true) - ); + onFailure: static (Exception _, object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: false), + onCancellation: (object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: true) +, + connectionToDoom: _connection.GetOpenTdsConnection()); return source.Task; } @@ -2753,25 +2746,26 @@ private Task CopyBatchesAsyncContinuedOnSuccess(BulkCopySimpleResultSet internal source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(writeTask, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(writeTask, source, this, + onSuccess: (object state) => { + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; try { - RunParser(); - CommitTransaction(); + sqlBulkCopy.RunParser(); + sqlBulkCopy.CommitTransaction(); } catch (Exception) { - CopyBatchesAsyncContinuedOnError(cleanupParser: false); + sqlBulkCopy.CopyBatchesAsyncContinuedOnError(cleanupParser: false); throw; } // Always call back into CopyBatchesAsync - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); }, - connectionToDoom: _connection.GetOpenTdsConnection(), - onFailure: _ => CopyBatchesAsyncContinuedOnError(cleanupParser: false) + onFailure: static (Exception _, object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: false), + connectionToDoom: _connection.GetOpenTdsConnection() ); return source.Task; } @@ -2906,16 +2900,17 @@ private void WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet int { source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; // Bulk copy task is completed at this moment. if (task.IsCanceled) { - _localColumnMappings = null; + sqlBulkCopy._localColumnMappings = null; try { - CleanUpStateObject(); + sqlBulkCopy.CleanUpStateObject(); } finally { @@ -2928,10 +2923,10 @@ private void WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet int } else { - _localColumnMappings = null; + sqlBulkCopy._localColumnMappings = null; try { - CleanUpStateObject(isCancelRequested: false); + sqlBulkCopy.CleanUpStateObject(isCancelRequested: false); } finally { @@ -3034,22 +3029,22 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio { if (_isAsyncBulkCopy) { - CancellationTokenRegistration regReconnectCancel = new CancellationTokenRegistration(); + StrongBox regReconnectCancel = new StrongBox(new CancellationTokenRegistration()); TaskCompletionSource cancellableReconnectTS = new TaskCompletionSource(); if (cts.CanBeCanceled) { - regReconnectCancel = cts.Register(() => cancellableReconnectTS.TrySetCanceled()); + regReconnectCancel.Value = cts.Register(() => cancellableReconnectTS.TrySetCanceled()); } - AsyncHelper.ContinueTask(reconnectTask, cancellableReconnectTS, - onSuccess: () => { cancellableReconnectTS.SetResult(null); } + AsyncHelper.ContinueTaskWithState(reconnectTask, cancellableReconnectTS, cancellableReconnectTS, + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); // No need to cancel timer since SqlBulkCopy creates specific task source for reconnection AsyncHelper.SetTimeoutException(cancellableReconnectTS, BulkCopyTimeout, () => { return SQL.BulkLoadInvalidDestinationTable(_destinationTableName, SQL.CR_ReconnectTimeout()); }, CancellationToken.None); - AsyncHelper.ContinueTask(cancellableReconnectTS.Task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(cancellableReconnectTS.Task, source, regReconnectCancel, + onSuccess: (object state) => { - regReconnectCancel.Dispose(); + ((StrongBox)state).Value.Dispose(); if (_parserLock != null) { _parserLock.Release(); @@ -3060,9 +3055,9 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio WriteToServerInternalRestAsync(cts, source); }, connectionToAbort: _connection, - onFailure: (e) => { regReconnectCancel.Dispose(); }, - onCancellation: () => { regReconnectCancel.Dispose(); }, - exceptionConverter: (ex) => SQL.BulkLoadInvalidDestinationTable(_destinationTableName, ex) + onFailure: static (Exception _, object state) => ((StrongBox)state).Value.Dispose(), + onCancellation: static (object state) => ((StrongBox)state).Value.Dispose(), + exceptionConverter: (Exception ex, object state) => SQL.BulkLoadInvalidDestinationTable(_destinationTableName, ex) ); return; } @@ -3070,7 +3065,7 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio { try { - AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, static () => throw SQL.CR_ReconnectTimeout()); } catch (SqlException ex) { @@ -3111,8 +3106,8 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio if (internalResultsTask != null) { - AsyncHelper.ContinueTask(internalResultsTask, source, - onSuccess: () => WriteToServerInternalRestContinuedAsync(internalResultsTask.Result, cts, source), + AsyncHelper.ContinueTaskWithState(internalResultsTask, source, this, + onSuccess: (object state) => ((SqlBulkCopy)state).WriteToServerInternalRestContinuedAsync(internalResultsTask.Result, cts, source), connectionToDoom: _connection.GetOpenTdsConnection() ); } @@ -3185,16 +3180,17 @@ private Task WriteToServerInternalAsync(CancellationToken ctoken) else { Debug.Assert(_isAsyncBulkCopy, "Read must not return a Task in the Sync mode"); - AsyncHelper.ContinueTask(readTask, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(readTask, source, this, + onSuccess: (object state) => { - if (!_hasMoreRowToCopy) + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + if (!sqlBulkCopy._hasMoreRowToCopy) { source.SetResult(null); // No rows to copy! } else { - WriteToServerInternalRestAsync(ctoken, source); // Passing the same completion which will be completed by the Callee. + sqlBulkCopy.WriteToServerInternalRestAsync(ctoken, source); // Passing the same completion which will be completed by the Callee. } }, connectionToDoom: _connection.GetOpenTdsConnection() diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index b0f8dc3d50..19fd0902f3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -1543,7 +1543,7 @@ private IAsyncResult BeginExecuteNonQueryInternal(CommandBehavior behavior, Asyn Task execNQ = InternalExecuteNonQuery(localCompletion, ADP.BeginExecuteNonQuery, false, timeout, out usedCache, asyncWrite, inRetry: inRetry); if (execNQ != null) { - AsyncHelper.ContinueTask(execNQ, localCompletion, () => BeginExecuteNonQueryInternalReadStage(localCompletion)); + AsyncHelper.ContinueTaskWithState(execNQ, localCompletion, this, (object state) => ((SqlCommand)state).BeginExecuteNonQueryInternalReadStage(localCompletion)); } else { @@ -2153,7 +2153,7 @@ private IAsyncResult BeginExecuteXmlReaderInternal(CommandBehavior behavior, Asy if (writeTask != null) { - AsyncHelper.ContinueTask(writeTask, localCompletion, () => BeginExecuteXmlReaderInternalReadStage(localCompletion)); + AsyncHelper.ContinueTaskWithState(writeTask, localCompletion, this, (object state) => ((SqlCommand)state).BeginExecuteXmlReaderInternalReadStage(localCompletion)); } else { @@ -2642,7 +2642,7 @@ private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncC if (writeTask != null) { - AsyncHelper.ContinueTask(writeTask, localCompletion, () => BeginExecuteReaderInternalReadStage(localCompletion)); + AsyncHelper.ContinueTaskWithState(writeTask, localCompletion, this, (object state) => ((SqlCommand)state).BeginExecuteReaderInternalReadStage(localCompletion)); } else { @@ -2982,14 +2982,19 @@ private Task InternalExecuteNonQueryAsync(CancellationToken cancellationTok /// protected override Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { - return ExecuteReaderAsync(behavior, cancellationToken).ContinueWith((result) => - { - if (result.IsFaulted) + return ExecuteReaderAsync(behavior, cancellationToken).ContinueWith( + static (Task result) => { - throw result.Exception.InnerException; - } - return result.Result; - }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); + if (result.IsFaulted) + { + throw result.Exception.InnerException; + } + return result.Result; + }, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, + TaskScheduler.Default + ); } private Task InternalExecuteReaderWithRetryAsync(CommandBehavior behavior, CancellationToken cancellationToken) @@ -3802,7 +3807,7 @@ private Task RunExecuteNonQueryTds(string methodName, bool async, int timeout, b _activeConnection.RegisterWaitingForReconnect(completion.Task); _reconnectionCompletionSource = completion; CancellationTokenSource timeoutCTS = new CancellationTokenSource(); - AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token); + AsyncHelper.SetTimeoutException(completion, timeout, static () => SQL.CR_ReconnectTimeout(), timeoutCTS.Token); AsyncHelper.ContinueTask(reconnectTask, completion, () => { @@ -3819,14 +3824,16 @@ private Task RunExecuteNonQueryTds(string methodName, bool async, int timeout, b } else { - AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null)); + AsyncHelper.ContinueTaskWithState(subTask, completion, completion, static (object state) => ((TaskCompletionSource)state).SetResult(null)); } - }, connectionToAbort: _activeConnection); + }, + connectionToAbort: _activeConnection + ); return completion.Task; } else { - AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, timeout, static () => throw SQL.CR_ReconnectTimeout()); timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart); } } @@ -5164,39 +5171,32 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior { long parameterEncryptionStart = ADP.TimerCurrent(); TaskCompletionSource completion = new TaskCompletionSource(); - AsyncHelper.ContinueTask(describeParameterEncryptionTask, completion, - () => + AsyncHelper.ContinueTaskWithState(describeParameterEncryptionTask, completion, this, + (object state) => { + SqlCommand command = (SqlCommand)state; Task subTask = null; - GenerateEnclavePackage(); - RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds); + command.GenerateEnclavePackage(); + command.RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds); if (subTask == null) { completion.SetResult(null); } else { - AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null)); + AsyncHelper.ContinueTaskWithState(subTask, completion, completion, static (object state2) => ((TaskCompletionSource)state2).SetResult(null)); } - }, connectionToDoom: null, - onFailure: ((exception) => + }, + onFailure: static (Exception exception, object state) => { - if (_cachedAsyncState != null) - { - _cachedAsyncState.ResetAsyncState(); - } + ((SqlCommand)state)._cachedAsyncState?.ResetAsyncState(); if (exception != null) { throw exception; } - }), - onCancellation: (() => - { - if (_cachedAsyncState != null) - { - _cachedAsyncState.ResetAsyncState(); - } - }), + }, + onCancellation: static (object state) => ((SqlCommand)state)._cachedAsyncState?.ResetAsyncState(), + connectionToDoom: null, connectionToAbort: _activeConnection); task = completion.Task; return ds; @@ -5265,7 +5265,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi _activeConnection.RegisterWaitingForReconnect(completion.Task); _reconnectionCompletionSource = completion; CancellationTokenSource timeoutCTS = new CancellationTokenSource(); - AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token); + AsyncHelper.SetTimeoutException(completion, timeout, static () => SQL.CR_ReconnectTimeout(), timeoutCTS.Token); AsyncHelper.ContinueTask(reconnectTask, completion, () => { @@ -5283,15 +5283,17 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi } else { - AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null)); + AsyncHelper.ContinueTaskWithState(subTask, completion, completion, static (object state) => ((TaskCompletionSource)state).SetResult(null)); } - }, connectionToAbort: _activeConnection); + }, + connectionToAbort: _activeConnection + ); task = completion.Task; return ds; } else { - AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, timeout, static () => throw SQL.CR_ReconnectTimeout()); timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index eb54272de3..76a992becc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -5090,7 +5090,7 @@ private Task GetBytesAsyncReadDataStage(int i, byte[] buffer, int index, in else { // setup for cleanup\completing - retryTask.ContinueWith((t) => CompleteRetryable(t, source, timeoutCancellationSource), TaskScheduler.Default); + retryTask.ContinueWith((Task t) => CompleteRetryable(t, source, timeoutCancellationSource), TaskScheduler.Default); return source.Task; } } @@ -5654,7 +5654,7 @@ private Task InvokeRetryable(Func> moreFunc, TaskCompletionS } else { - task.ContinueWith((t) => CompleteRetryable(t, source, objectToDispose), TaskScheduler.Default); + task.ContinueWith((Task t) => CompleteRetryable(t, source, objectToDispose), TaskScheduler.Default); } } catch (AggregateException e) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 3dd69c5299..868f9eaf9a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -33,8 +33,14 @@ internal static Task CreateContinuationTask(Task task, Action onSuccess, SqlInte { TaskCompletionSource completion = new TaskCompletionSource(); ContinueTask(task, completion, - () => { onSuccess(); completion.SetResult(null); }, - connectionToDoom, onFailure); + onSuccess: () => + { + onSuccess(); + completion.SetResult(null); + }, + onFailure: onFailure, + connectionToDoom: connectionToDoom + ); return completion.Task; } } @@ -45,14 +51,14 @@ internal static Task CreateContinuationTask(Task task, Action onSuccess, SqlInte } internal static void ContinueTask(Task task, - TaskCompletionSource completion, - Action onSuccess, - SqlInternalConnectionTds connectionToDoom = null, - Action onFailure = null, - Action onCancellation = null, - Func exceptionConverter = null, - SqlConnection connectionToAbort = null - ) + TaskCompletionSource completion, + Action onSuccess, + Action onFailure = null, + Action onCancellation = null, + Func exceptionConverter = null, + SqlInternalConnectionTds connectionToDoom = null, + SqlConnection connectionToAbort = null + ) { Debug.Assert((connectionToAbort == null) || (connectionToDoom == null), "Should not specify both connectionToDoom and connectionToAbort"); task.ContinueWith( @@ -172,6 +178,132 @@ internal static Task CreateContinuationTask(Task task, Action onSuccess, SqlInte ); } + internal static void ContinueTaskWithState(Task task, + TaskCompletionSource completion, + object state, + Action onSuccess, + Action onFailure = null, + Action onCancellation = null, + Func exceptionConverter = null, + SqlInternalConnectionTds connectionToDoom = null, + SqlConnection connectionToAbort = null + ) + { + Debug.Assert((connectionToAbort == null) || (connectionToDoom == null), "Should not specify both connectionToDoom and connectionToAbort"); + task.ContinueWith( + (Task tsk, object state) => + { + if (tsk.Exception != null) + { + Exception exc = tsk.Exception.InnerException; + if (exceptionConverter != null) + { + exc = exceptionConverter(exc, state); + } + try + { + onFailure?.Invoke(exc, state); + } + finally + { + completion.TrySetException(exc); + } + } + else if (tsk.IsCanceled) + { + try + { + onCancellation?.Invoke(state); + } + finally + { + completion.TrySetCanceled(); + } + } + else + { + if (connectionToDoom != null || connectionToAbort != null) + { + RuntimeHelpers.PrepareConstrainedRegions(); + try + { +#if DEBUG + TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + tdsReliabilitySection.Start(); +#endif //DEBUG + onSuccess(state); +#if DEBUG + } + finally + { + tdsReliabilitySection.Stop(); + } +#endif //DEBUG + } + catch (System.OutOfMemoryException e) + { + if (connectionToDoom != null) + { + connectionToDoom.DoomThisConnection(); + } + else + { + connectionToAbort.Abort(e); + } + completion.SetException(e); + throw; + } + catch (System.StackOverflowException e) + { + if (connectionToDoom != null) + { + connectionToDoom.DoomThisConnection(); + } + else + { + connectionToAbort.Abort(e); + } + completion.SetException(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + if (connectionToDoom != null) + { + connectionToDoom.DoomThisConnection(); + } + else + { + connectionToAbort.Abort(e); + } + completion.SetException(e); + throw; + } + catch (Exception e) + { + completion.SetException(e); + } + } + else + { // no connection to doom - reliability section not required + try + { + onSuccess(state); + } + catch (Exception e) + { + completion.SetException(e); + } + } + } + }, + state: state, + scheduler: TaskScheduler.Default + ); + } internal static void WaitForCompletion(Task task, int timeout, Action onTimeout = null, bool rethrowExceptions = true) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index f1a714f319..92482d9e83 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -10389,19 +10389,25 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques task = completion.Task; } - AsyncHelper.ContinueTask(writeParamTask, completion, - () => TdsExecuteRPC(cmd, rpcArray, timeout, inSchema, notificationRequest, stateObj, isCommandProc, sync, completion, - startRpc: ii, startParam: i + 1), - connectionToDoom: _connHandler, - onFailure: exc => TdsExecuteRPC_OnFailure(exc, stateObj)); + AsyncHelper.ContinueTaskWithState(writeParamTask, completion, this, + (object state) => + { + TdsParser tdsParser = (TdsParser)state; + TdsExecuteRPC(cmd, rpcArray, timeout, inSchema, notificationRequest, stateObj, isCommandProc, sync, completion, + startRpc: ii, startParam: i + 1); + }, + onFailure: (Exception exc, object state) => ((TdsParser)state).TdsExecuteRPC_OnFailure(exc, stateObj), + connectionToDoom: _connHandler + ); // Take care of releasing the locks if (releaseConnectionLock) { - task.ContinueWith(_ => - { - _connHandler._parserLock.Release(); - }, TaskScheduler.Default); + task.ContinueWith( + static (Task _, object state) => ((TdsParser)state)._connHandler._parserLock.Release(), + state: this, + scheduler: TaskScheduler.Default + ); releaseConnectionLock = false; } @@ -11960,7 +11966,8 @@ private Task GetTerminationTask(Task unterminatedWriteTask, object value, MetaTy { return AsyncHelper.CreateContinuationTask(unterminatedWriteTask, WriteInt, 0, stateObj, - connectionToDoom: _connHandler); + connectionToDoom: _connHandler + ); } } else diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 9453f6102a..131b22088c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -3361,11 +3361,12 @@ internal Task WriteByteArray(Byte[] b, int len, int offsetBuffer, bool canAccumu } // This is in its own method to avoid always allocating the lambda in WriteByteArray - private void WriteByteArraySetupContinuation(Byte[] b, int len, TaskCompletionSource completion, int offset, Task packetTask) + private void WriteByteArraySetupContinuation(byte[] b, int len, TaskCompletionSource completion, int offset, Task packetTask) { AsyncHelper.ContinueTask(packetTask, completion, () => WriteByteArray(b, len: len, offsetBuffer: offset, canAccumulate: false, completion: completion), - connectionToDoom: _parser.Connection); + connectionToDoom: _parser.Connection + ); } // Dumps contents of buffer to SNI for network write. From f188cef29608fb54330d5a45cd636090d7b89f48 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 14 Jun 2021 11:42:03 -0700 Subject: [PATCH 68/87] Update nuspec for AKV Provider (#1111) --- ...a.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec index 9667ff7fd0..e52a11072f 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -25,21 +25,21 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti sqlclient microsoft.data.sqlclient azurekeyvaultprovider akvprovider alwaysencrypted - + - + - + From 2dec1e95c24a4d5ceaae51605cb426d5000db53d Mon Sep 17 00:00:00 2001 From: Javad Date: Mon, 14 Jun 2021 14:57:36 -0700 Subject: [PATCH 69/87] buildGuide (#1114) --- BUILDGUIDE.md | 100 +++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index 3cddb58e7b..767c9b8523 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -14,87 +14,87 @@ Once the environment is setup properly, execute the desired set of commands belo ```bash # Default Build Configuration: -> msbuild +msbuild # Builds the driver for the Client OS in 'Debug' Configuration for 'AnyCPU' platform. # Both .NET Framework (NetFx) and .NET Core drivers are built by default (as supported by Client OS). ``` ```bash -> msbuild /p:Configuration=Release +msbuild /p:Configuration=Release # Builds the driver in 'Release' Configuration for `AnyCPU` platform. ``` ```bash -> msbuild /p:Platform=Win32 +msbuild /p:Platform=Win32 # Builds the .NET Framework (NetFx) driver for Win32 (x86) platform on Windows in 'Debug' Configuration. ``` ```bash -> msbuild /t:clean +msbuild /t:clean # Cleans all build directories. ``` ```bash -> msbuild /t:restore +msbuild /t:restore # Restores Nuget Packages. ``` ```bash -> msbuild /t:BuildAllConfigurations +msbuild /t:BuildAllConfigurations # Builds the driver for all target OSes and supported platforms. ``` ```bash -> msbuild /p:BuildNetFx=false +msbuild /p:BuildNetFx=false # Skips building the .NET Framework (NetFx) Driver on Windows. # On Unix the netfx driver build is automatically skipped. ``` ```bash -> msbuild /p:OSGroup=Unix +msbuild /p:OSGroup=Unix # Builds the driver for the Unix platform. ``` ```bash -> msbuild /t:BuildNetCoreAllOS +msbuild /t:BuildNetCoreAllOS # Builds the .NET Core driver for all Operating Systems. ``` ## Building Tests ```bash -> msbuild /t:BuildTestsNetCore +msbuild /t:BuildTestsNetCore # Build the tests for the .NET Core driver in 'Debug' Configuration. Default .NET Core version is 2.1. ``` ```bash -> msbuild /t:BuildTestsNetFx +msbuild /t:BuildTestsNetFx # Build the tests for the .NET Framework (NetFx) driver in 'Debug' Configuration. Default .NET Framework version is 4.6.1. ``` ## Run Functional Tests - Windows (`netfx x86`): - ```bash - > dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" - ``` +```bash +dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" +``` - Windows (`netfx x64`): - ```bash - > dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" - ``` +```bash +dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" +``` - AnyCPU: Windows (`netcoreapp`): - ```bash - > dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" - ``` +```bash +dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" +``` Unix (`netcoreapp`): - ```bash - > dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" - ``` +```bash +dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" +``` ## Run Manual Tests @@ -126,35 +126,35 @@ Manual Tests require the below setup to run: ### Commands to run Manual Tests: - Windows (`netfx x86`): - ```bash - > dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" - ``` +```bash +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" + ``` - Windows (`netfx x64`): - ```bash - > dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" - ``` +```bash +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" +``` - AnyCPU: Windows (`netfx`): - ```bash - > dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" - ``` +```bash +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" +``` Windows (`netcoreapp`): - ```bash - > dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" - ``` +```bash +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" +``` Unix (`netcoreapp`): - ```bash - > dotnet test "src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" - ``` +```bash +dotnet test "src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" +``` ## Run A Single Test ```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "FullyQualifiedName=Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.CspProviderExt.TestKeysFromCertificatesCreatedWithMultipleCryptoProviders" +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "FullyQualifiedName=Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.CspProviderExt.TestKeysFromCertificatesCreatedWithMultipleCryptoProviders" ``` ## Testing with Custom ReferenceType @@ -169,7 +169,7 @@ Tests can be built and run with custom "Reference Type" property that enables di > ************** IMPORTANT NOTE BEFORE PROCEEDING WITH "PACKAGE" AND "NETSTANDARDPACKAGE" REFERENCE TYPES *************** > CREATE A NUGET PACKAGE WITH BELOW COMMAND AND ADD TO LOCAL FOLDER + UPDATE NUGET CONFIG FILE TO READ FROM THAT LOCATION > ``` -> > msbuild /p:configuration=Release +> msbuild /p:configuration=Release > ``` ### Building Tests: @@ -177,23 +177,23 @@ Tests can be built and run with custom "Reference Type" property that enables di For .NET Core, all 4 reference types are supported: ```bash -> msbuild /t:BuildTestsNetCore /p:ReferenceType=Project +msbuild /t:BuildTestsNetCore /p:ReferenceType=Project # Default setting uses Project Reference. -> msbuild /t:BuildTestsNetCore /p:ReferenceType=Package +msbuild /t:BuildTestsNetCore /p:ReferenceType=Package -> msbuild /t:BuildTestsNetCore /p:ReferenceType=NetStandard +msbuild /t:BuildTestsNetCore /p:ReferenceType=NetStandard -> msbuild /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage +msbuild /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage ``` For .NET Framework, below reference types are supported: ```bash -> msbuild /t:BuildTestsNetFx /p:ReferenceType=Project +msbuild /t:BuildTestsNetFx /p:ReferenceType=Project # Default setting uses Project Reference. -> msbuild /t:BuildTestsNetFx /p:ReferenceType=Package +msbuild /t:BuildTestsNetFx /p:ReferenceType=Package ``` ### Running Tests: @@ -210,13 +210,13 @@ Tests can be built and run with custom Target Frameworks. See the below examples ### Building Tests: ```bash -> msbuild /t:BuildTestsNetFx /p:TargetNetFxVersion=net462 +msbuild /t:BuildTestsNetFx /p:TargetNetFxVersion=net462 # Build the tests for custom TargetFramework (.NET Framework) # Applicable values: net461 (Default) | net462 | net47 | net471 net472 | net48 ``` ```bash -> msbuild /t:BuildTestsNetCore /p:TargetNetCoreVersion=netcoreapp3.1 +msbuild /t:BuildTestsNetCore /p:TargetNetCoreVersion=netcoreapp3.1 # Build the tests for custom TargetFramework (.NET Core) # Applicable values: netcoreapp2.1 | netcoreapp2.2 | netcoreapp3.1 | net5.0 ``` @@ -224,11 +224,11 @@ Tests can be built and run with custom Target Frameworks. See the below examples ### Running Tests: ```bash -> dotnet test /p:TargetNetFxVersion=net462 ... +dotnet test /p:TargetNetFxVersion=net462 ... # Use above property to run Functional Tests with custom TargetFramework (.NET Framework) # Applicable values: net461 (Default) | net462 | net47 | net471 net472 | net48 -> dotnet test /p:TargetNetCoreVersion=netcoreapp3.1 ... +dotnet test /p:TargetNetCoreVersion=netcoreapp3.1 ... # Use above property to run Functional Tests with custom TargetFramework (.NET Core) # Applicable values: netcoreapp2.1 | netcoreapp2.2 | netcoreapp3.1 | net5.0 ``` From c99ce8d23f359860914bc537a85a215be81c8bab Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 14 Jun 2021 14:59:54 -0700 Subject: [PATCH 70/87] AKV provider v3.0.0 release notes (#1113) --- release-notes/README.md | 3 +- .../AzureKeyVaultProvider/2.0/2.0.0.md | 8 +-- .../AzureKeyVaultProvider/3.0/3.0.0.md | 69 +++++++++++++++++++ .../AzureKeyVaultProvider/3.0/README.md | 7 ++ 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 release-notes/add-ons/AzureKeyVaultProvider/3.0/3.0.0.md create mode 100644 release-notes/add-ons/AzureKeyVaultProvider/3.0/README.md diff --git a/release-notes/README.md b/release-notes/README.md index 0bd2dfa84b..bc1847a545 100644 --- a/release-notes/README.md +++ b/release-notes/README.md @@ -12,10 +12,11 @@ The latest stable release is [Microsoft.Data.SqlClient 3.0](3.0). # Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider Release Notes -The latest stable release is [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 2.0](add-ons/AzureKeyVaultProvider/2.0). +The latest stable release is [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0](add-ons/AzureKeyVaultProvider/3.0). ## Release Information +- [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0](add-ons/AzureKeyVaultProvider/3.0) - [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 2.0](add-ons/AzureKeyVaultProvider/2.0) - [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.2](add-ons/AzureKeyVaultProvider/1.2) - [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.1](add-ons/AzureKeyVaultProvider/1.1) diff --git a/release-notes/add-ons/AzureKeyVaultProvider/2.0/2.0.0.md b/release-notes/add-ons/AzureKeyVaultProvider/2.0/2.0.0.md index 1884d59a85..b798c2e581 100644 --- a/release-notes/add-ons/AzureKeyVaultProvider/2.0/2.0.0.md +++ b/release-notes/add-ons/AzureKeyVaultProvider/2.0/2.0.0.md @@ -19,10 +19,10 @@ This library contains the implementation of `Microsoft.Data.SqlClient.SqlColumnE Once the provider is registered, it can be used to perform Always Encrypted operations by creating a Column Master Key using the Azure Key Vault Key Identifier URL. The linked C# samples below demonstrate using Always Encrypted with secure enclaves with Azure Key Vault: -- Legacy API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/master/doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs) -- New API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/master/doc/samples/AzureKeyVaultProviderExample_2_0.cs) -- Legacy API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/master/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample.cs) -- New API support (Always Encrypted with secure snclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/master/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample_2_0.cs) +- Legacy API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs) +- New API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProviderExample_2_0.cs) +- Legacy API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample.cs) +- New API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample_2_0.cs) ## Target Platform Support diff --git a/release-notes/add-ons/AzureKeyVaultProvider/3.0/3.0.0.md b/release-notes/add-ons/AzureKeyVaultProvider/3.0/3.0.0.md new file mode 100644 index 0000000000..cc1f90eac2 --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/3.0/3.0.0.md @@ -0,0 +1,69 @@ +# Release Notes + +## General Availability of Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider + +_**3.0.0 released 14 June 2021**_ + +This library contains the implementation of `Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider` for accessing Azure Key Vault, and the provider class is named `SqlColumnEncryptionAzureKeyVaultProvider`. + +### Added + +- Introduces column encryption key caching support [#1056](https://github.com/dotnet/SqlClient/pull/1056) + +### Breaking Changes + +- Microsoft.Data.SqlClient dependency version upgraded to **v3.0.0+** [#1111](https://github.com/dotnet/SqlClient/pull/1111) + +### Working with SQLColumnEncryptionAzureKeyVaultProvider + +`SqlColumnEncryptionAzureKeyVaultProvider` **v3.0** is implemented against `Microsoft.Data.SqlClient` **v3.0** and supports .NET Framework 4.6.1+, .NET Core 2.1+, and .NET Standard 2.0+. The provider name identifier for this library is "**AZURE_KEY_VAULT**" and it is not registered in the driver by default. Client applications may initialize this provider by providing an `Azure.Core.TokenCredential` and registering it with the driver using any of the below APIs: + +- [SqlConnection.RegisterColumnEncryptionKeyStoreProviders](https://docs.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlconnection.registercolumnencryptionkeystoreproviders?view=sqlclient-dotnet-3.0) +- [SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection](https://docs.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlconnection.registercolumnencryptionkeystoreprovidersonconnection?view=sqlclient-dotnet-3.0) (Added in version 3.0.0) +- [SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand](https://docs.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlcommand.registercolumnencryptionkeystoreprovidersoncommand?view=sqlclient-dotnet-3.0) (Added in version 3.0.0) + +Once the provider is registered, it can be used to perform Always Encrypted operations by creating a Column Master Key using the Azure Key Vault Key Identifier URL. + +The linked C# samples below demonstrate using Always Encrypted with secure enclaves with Azure Key Vault: + +- Legacy API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs) +- New API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProviderExample_2_0.cs) +- Legacy API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample.cs) +- New API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample_2_0.cs) +- Column Encryption Key cache scope example: [AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs) +- Registering custom key store provider - Connection Precedence: [RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs) +- Registering custom key store provider - Command Precedence: [RegisterCustomKeyStoreProvider_CommandPrecedence.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs) + +For further details, refer to [Using the Azure Key Vault provider](https://docs.microsoft.com/sql/connect/ado-net/sql/sqlclient-support-always-encrypted#using-the-azure-key-vault-provider) + +## Target Platform Support + +- .NET Framework 4.6.1+ +- .NET Core 2.1+ (Windows x86, Windows x64, Linux, macOS) +- .NET Standard 2.0+ + +### Dependencies + +#### .NET Framework + +- Azure.Core 1.6.0 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 3.0.0 +- System.Text.Encodings.Web 4.7.2 +- Microsoft.Extensions.Caching.Memory 5.0.0 + +#### .NET Core + +- Azure.Core 1.6.0 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 3.0.0 +- System.Text.Encodings.Web 4.7.2 +- Microsoft.Extensions.Caching.Memory 5.0.0 + +#### .NET Standard + +- Azure.Core 1.6.0 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 3.0.0 +- System.Text.Encodings.Web 4.7.2 +- Microsoft.Extensions.Caching.Memory 5.0.0 diff --git a/release-notes/add-ons/AzureKeyVaultProvider/3.0/README.md b/release-notes/add-ons/AzureKeyVaultProvider/3.0/README.md new file mode 100644 index 0000000000..d7deee4b63 --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/3.0/README.md @@ -0,0 +1,7 @@ +# Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0 Releases + +The following Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0 stable releases have been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2021/06/14 | 3.0.0 | [release notes](3.0.0.md) | From 218e34f6aafdd3025528b68250d8c9fcdee9dbef Mon Sep 17 00:00:00 2001 From: Wraith Date: Mon, 14 Jun 2021 23:05:00 +0100 Subject: [PATCH 71/87] Simplify SNIProxy (#934) --- .../Microsoft/Data/SqlClient/SNI/SNIProxy.cs | 165 +----------------- .../Data/SqlClient/TdsParser.Unix.cs | 2 +- .../Data/SqlClient/TdsParser.Windows.cs | 2 +- .../SqlClient/TdsParserStateObjectManaged.cs | 93 ++++++++-- 4 files changed, 87 insertions(+), 175 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs index 5823e7f44c..24c8609876 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs @@ -21,48 +21,9 @@ internal class SNIProxy private const int DefaultSqlServerDacPort = 1434; private const string SqlServerSpnHeader = "MSSQLSvc"; - internal class SspiClientContextResult - { - internal const uint OK = 0; - internal const uint Failed = 1; - internal const uint KerberosTicketMissing = 2; - } - - internal static readonly SNIProxy s_singleton = new SNIProxy(); + private static readonly SNIProxy s_singleton = new SNIProxy(); - internal static SNIProxy GetInstance() => s_singleton; - - /// - /// Enable SSL on a connection - /// - /// Connection handle - /// - /// SNI error code - internal uint EnableSsl(SNIHandle handle, uint options) - { - try - { - SqlClientEventSource.Log.TryTraceEvent("SNIProxy.EnableSsl | Info | Session Id {0}", handle?.ConnectionId); - return handle.EnableSsl(options); - } - catch (Exception e) - { - SqlClientEventSource.Log.TryTraceEvent("SNIProxy.EnableSsl | Err | Session Id {0}, SNI Handshake failed with exception: {1}", handle?.ConnectionId, e?.Message); - return SNICommon.ReportSNIError(SNIProviders.SSL_PROV, SNICommon.HandshakeFailureError, e); - } - } - - /// - /// Disable SSL on a connection - /// - /// Connection handle - /// SNI error code - internal uint DisableSsl(SNIHandle handle) - { - SqlClientEventSource.Log.TryTraceEvent("SNIProxy.DisableSsl | Info | Session Id {0}", handle?.ConnectionId); - handle.DisableSsl(); - return TdsEnums.SNI_SUCCESS; - } + internal static SNIProxy Instance => s_singleton; /// /// Generate SSPI context @@ -72,7 +33,7 @@ internal uint DisableSsl(SNIHandle handle) /// Send buffer /// Service Principal Name buffer /// SNI error code - internal void GenSspiClientContext(SspiClientContextStatus sspiClientContextStatus, byte[] receivedBuff, ref byte[] sendBuff, byte[][] serverName) + internal static void GenSspiClientContext(SspiClientContextStatus sspiClientContextStatus, byte[] receivedBuff, ref byte[] sendBuff, byte[][] serverName) { SafeDeleteContext securityContext = sspiClientContextStatus.SecurityContext; ContextFlagsPal contextFlags = sspiClientContextStatus.ContextFlags; @@ -165,83 +126,6 @@ private static bool IsErrorStatus(SecurityStatusPalErrorCode errorCode) errorCode != SecurityStatusPalErrorCode.Renegotiate; } - /// - /// Set connection buffer size - /// - /// SNI handle - /// Buffer size - /// SNI error code - internal uint SetConnectionBufferSize(SNIHandle handle, uint bufferSize) - { - handle.SetBufferSize((int)bufferSize); - return TdsEnums.SNI_SUCCESS; - } - - /// - /// Copies data in SNIPacket to given byte array parameter - /// - /// SNIPacket object containing data packets - /// Destination byte array where data packets are copied to - /// Length of data packets - /// SNI error status - internal uint PacketGetData(SNIPacket packet, byte[] inBuff, ref uint dataSize) - { - int dataSizeInt = 0; - packet.GetData(inBuff, ref dataSizeInt); - dataSize = (uint)dataSizeInt; - - return TdsEnums.SNI_SUCCESS; - } - - /// - /// Read synchronously - /// - /// SNI handle - /// SNI packet - /// Timeout - /// SNI error status - internal uint ReadSyncOverAsync(SNIHandle handle, out SNIPacket packet, int timeout) - { - return handle.Receive(out packet, timeout); - } - - /// - /// Get SNI connection ID - /// - /// SNI handle - /// Client connection ID - /// SNI error status - internal uint GetConnectionId(SNIHandle handle, ref Guid clientConnectionId) - { - clientConnectionId = handle.ConnectionId; - SqlClientEventSource.Log.TryTraceEvent("SNIProxy.GetConnectionId | Info | Session Id {0}", clientConnectionId); - return TdsEnums.SNI_SUCCESS; - } - - /// - /// Send a packet - /// - /// SNI handle - /// SNI packet - /// true if synchronous, false if asynchronous - /// SNI error status - internal uint WritePacket(SNIHandle handle, SNIPacket packet, bool sync) - { - uint result; - if (sync) - { - result = handle.Send(packet); - handle.ReturnPacket(packet); - } - else - { - result = handle.SendAsync(packet); - } - - SqlClientEventSource.Log.TryTraceEvent("SNIProxy.WritePacket | Info | Session Id {0}, SendAsync Result {1}", handle?.ConnectionId, result); - return result; - } - /// /// Create a SNI connection handle /// @@ -258,7 +142,7 @@ internal uint WritePacket(SNIHandle handle, SNIPacket packet, bool sync) /// Used for DNS Cache /// Used for DNS Cache /// SNI handle - internal SNIHandle CreateConnectionHandle(string fullServerName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, + internal static SNIHandle CreateConnectionHandle(string fullServerName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool parallel, bool isIntegratedSecurity, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { instanceName = new byte[1]; @@ -380,7 +264,7 @@ private static byte[][] GetSqlServerSPNs(string hostNameOrAddress, string portOr /// Key for DNS Cache /// Used for DNS Cache /// SNITCPHandle - private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool parallel, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + private static SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool parallel, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { // TCP Format: // tcp:\ @@ -421,8 +305,6 @@ private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool return new SNITCPHandle(hostName, port, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo); } - - /// /// Creates an SNINpHandle object /// @@ -430,7 +312,7 @@ private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool /// Timer expiration /// Should MultiSubnetFailover be used. Only returns an error for named pipes. /// SNINpHandle - private SNINpHandle CreateNpHandle(DataSource details, long timerExpire, bool parallel) + private static SNINpHandle CreateNpHandle(DataSource details, long timerExpire, bool parallel) { if (parallel) { @@ -441,39 +323,6 @@ private SNINpHandle CreateNpHandle(DataSource details, long timerExpire, bool pa return new SNINpHandle(details.PipeHostName, details.PipeName, timerExpire); } - /// - /// Read packet asynchronously - /// - /// SNI handle - /// Packet - /// SNI error status - internal uint ReadAsync(SNIHandle handle, out SNIPacket packet) - { - packet = null; - return handle.ReceiveAsync(ref packet); - } - - /// - /// Set packet data - /// - /// SNI packet - /// Data - /// Length - internal void PacketSetData(SNIPacket packet, byte[] data, int length) - { - packet.AppendData(data, length); - } - - /// - /// Check SNI handle connection - /// - /// - /// SNI error status - internal uint CheckConnection(SNIHandle handle) - { - return handle.CheckConnection(); - } - /// /// Get last SNI error on this thread /// @@ -489,7 +338,7 @@ internal SNIError GetLastError() /// The data source /// Set true when an error occurred while getting LocalDB up /// - private string GetLocalDBDataSource(string fullServerName, out bool error) + private static string GetLocalDBDataSource(string fullServerName, out bool error) { string localDBConnectionString = null; bool isBadLocalDBDataSource; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs index 692633efd6..2f242a083f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs @@ -26,7 +26,7 @@ private void WaitForSSLHandShakeToComplete(ref uint error, ref int protocolVersi private SNIErrorDetails GetSniErrorDetails() { SNIErrorDetails details; - SNIError sniError = SNIProxy.GetInstance().GetLastError(); + SNIError sniError = SNIProxy.Instance.GetLastError(); details.sniErrorNumber = sniError.sniError; details.errorMessage = sniError.errorMessage; details.nativeError = sniError.nativeError; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs index 07846127d7..f7a0363f27 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs @@ -95,7 +95,7 @@ private SNIErrorDetails GetSniErrorDetails() if (TdsParserStateObjectFactory.UseManagedSNI) { - SNIError sniError = SNIProxy.GetInstance().GetLastError(); + SNIError sniError = SNIProxy.Instance.GetLastError(); details.sniErrorNumber = sniError.sniError; details.errorMessage = sniError.errorMessage; details.nativeError = sniError.nativeError; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs index 6bf08b0336..4c3ad107b4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs @@ -47,13 +47,25 @@ internal SNIMarsHandle CreateMarsSession(object callbackObject, bool async) return _marsConnection.CreateMarsSession(callbackObject, async); } - protected override uint SNIPacketGetData(PacketHandle packet, byte[] _inBuff, ref uint dataSize) - => SNIProxy.GetInstance().PacketGetData(packet.ManagedPacket, _inBuff, ref dataSize); + /// + /// Copies data in SNIPacket to given byte array parameter + /// + /// SNIPacket object containing data packets + /// Destination byte array where data packets are copied to + /// Length of data packets + /// SNI error status + protected override uint SNIPacketGetData(PacketHandle packet, byte[] inBuff, ref uint dataSize) + { + int dataSizeInt = 0; + packet.ManagedPacket.GetData(inBuff, ref dataSizeInt); + dataSize = (uint)dataSizeInt; + return TdsEnums.SNI_SUCCESS; + } internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool parallel, SqlConnectionIPAddressPreference iPAddressPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity) { - _sessionHandle = SNIProxy.GetInstance().CreateConnectionHandle(serverName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref spnBuffer, flushCache, async, parallel, isIntegratedSecurity, + _sessionHandle = SNIProxy.CreateConnectionHandle(serverName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref spnBuffer, flushCache, async, parallel, isIntegratedSecurity, iPAddressPreference, cachedFQDN, ref pendingDNSInfo); if (_sessionHandle == null) { @@ -162,7 +174,9 @@ internal override PacketHandle ReadSyncOverAsync(int timeoutRemaining, out uint { throw ADP.ClosedConnectionError(); } - error = SNIProxy.GetInstance().ReadSyncOverAsync(handle, out SNIPacket packet, timeoutRemaining); + + error = handle.Receive(out SNIPacket packet, timeoutRemaining); + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.ReadSyncOverAsync | Info | State Object Id {0}, Session Id {1}", _objectID, _sessionHandle?.ConnectionId); #if DEBUG SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObjectManaged.ReadSyncOverAsync | TRC | State Object Id {0}, Session Id {1}, Packet {2} received, Packet owner Id {3}, Packet dataLeft {4}", _objectID, _sessionHandle?.ConnectionId, packet?._id, packet?._owner.ConnectionId, packet?.DataLeft); @@ -191,12 +205,14 @@ internal override void ReleasePacket(PacketHandle syncReadPacket) internal override uint CheckConnection() { SNIHandle handle = Handle; - return handle == null ? TdsEnums.SNI_SUCCESS : SNIProxy.GetInstance().CheckConnection(handle); + return handle == null ? TdsEnums.SNI_SUCCESS : handle.CheckConnection(); } internal override PacketHandle ReadAsync(SessionHandle handle, out uint error) { - error = SNIProxy.GetInstance().ReadAsync(handle.ManagedHandle, out SNIPacket packet); + SNIPacket packet = null; + error = handle.ManagedHandle.ReceiveAsync(ref packet); + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.ReadAsync | Info | State Object Id {0}, Session Id {1}, Packet DataLeft {2}", _objectID, _sessionHandle?.ConnectionId, packet?.DataLeft); return PacketHandle.FromManagedPacket(packet); } @@ -214,8 +230,24 @@ internal override PacketHandle CreateAndSetAttentionPacket() return packetHandle; } - internal override uint WritePacket(PacketHandle packet, bool sync) => - SNIProxy.GetInstance().WritePacket(Handle, packet.ManagedPacket, sync); + internal override uint WritePacket(PacketHandle packetHandle, bool sync) + { + uint result; + SNIHandle handle = Handle; + SNIPacket packet = packetHandle.ManagedPacket; + if (sync) + { + result = handle.Send(packet); + handle.ReturnPacket(packet); + } + else + { + result = handle.SendAsync(packet); + } + + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.WritePacket | Info | Session Id {0}, SendAsync Result {1}", handle?.ConnectionId, result); + return result; + } // No- Op in managed SNI internal override PacketHandle AddPacketToPendingList(PacketHandle packet) => packet; @@ -246,11 +278,25 @@ internal override void ClearAllWritePackets() Debug.Assert(_asyncWriteCount == 0, "Should not clear all write packets if there are packets pending"); } - internal override void SetPacketData(PacketHandle packet, byte[] buffer, int bytesUsed) => SNIProxy.GetInstance().PacketSetData(packet.ManagedPacket, buffer, bytesUsed); + internal override void SetPacketData(PacketHandle packet, byte[] buffer, int bytesUsed) + { + packet.ManagedPacket.AppendData(buffer, bytesUsed); + } - internal override uint SniGetConnectionId(ref Guid clientConnectionId) => SNIProxy.GetInstance().GetConnectionId(Handle, ref clientConnectionId); + internal override uint SniGetConnectionId(ref Guid clientConnectionId) + { + clientConnectionId = Handle.ConnectionId; + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.GetConnectionId | Info | Session Id {0}", clientConnectionId); + return TdsEnums.SNI_SUCCESS; + } - internal override uint DisableSsl() => SNIProxy.GetInstance().DisableSsl(Handle); + internal override uint DisableSsl() + { + SNIHandle handle = Handle; + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.DisableSsl | Info | Session Id {0}", handle?.ConnectionId); + handle.DisableSsl(); + return TdsEnums.SNI_SUCCESS; + } internal override uint EnableMars(ref uint info) { @@ -265,9 +311,26 @@ internal override uint EnableMars(ref uint info) return TdsEnums.SNI_ERROR; } - internal override uint EnableSsl(ref uint info) => SNIProxy.GetInstance().EnableSsl(Handle, info); + internal override uint EnableSsl(ref uint info) + { + SNIHandle handle = Handle; + try + { + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.EnableSsl | Info | Session Id {0}", handle?.ConnectionId); + return handle.EnableSsl(info); + } + catch (Exception e) + { + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.EnableSsl | Err | Session Id {0}, SNI Handshake failed with exception: {1}", handle?.ConnectionId, e?.Message); + return SNICommon.ReportSNIError(SNIProviders.SSL_PROV, SNICommon.HandshakeFailureError, e); + } + } - internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize) => SNIProxy.GetInstance().SetConnectionBufferSize(Handle, unsignedPacketSize); + internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize) + { + Handle.SetBufferSize((int)unsignedPacketSize); + return TdsEnums.SNI_SUCCESS; + } internal override uint GenerateSspiClientContext(byte[] receivedBuff, uint receivedLength, ref byte[] sendBuff, ref uint sendLength, byte[][] _sniSpnBuffer) { @@ -276,8 +339,8 @@ internal override uint GenerateSspiClientContext(byte[] receivedBuff, uint recei _sspiClientContextStatus = new SspiClientContextStatus(); } - SNIProxy.GetInstance().GenSspiClientContext(_sspiClientContextStatus, receivedBuff, ref sendBuff, _sniSpnBuffer); - SqlClientEventSource.Log.TryTraceEvent("SNIProxy.GenerateSspiClientContext | Info | Session Id {0}", _sessionHandle?.ConnectionId); + SNIProxy.GenSspiClientContext(_sspiClientContextStatus, receivedBuff, ref sendBuff, _sniSpnBuffer); + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.GenerateSspiClientContext | Info | Session Id {0}", _sessionHandle?.ConnectionId); sendLength = (uint)(sendBuff != null ? sendBuff.Length : 0); return 0; } From 4c3dcb998df8162fa2a6ac8cc9f75746e1aa81e4 Mon Sep 17 00:00:00 2001 From: Wraith Date: Mon, 14 Jun 2021 23:08:42 +0100 Subject: [PATCH 72/87] Perf: Change SqlParameter bool fields to flags (#1064) --- .../Microsoft/Data/SqlClient/SqlParameter.cs | 133 +++++++++++------- .../Microsoft/Data/SqlClient/SqlParameter.cs | 133 +++++++++++------- 2 files changed, 168 insertions(+), 98 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs index e01a5560ff..8f0549242b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -208,6 +208,22 @@ private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) } } + [Flags] + private enum SqlParameterFlags : ushort + { + None = 0, + IsNull = 1, + IsNullable = 2, + IsSqlParameterSqlType = 4, + SourceColumnNullMapping = 8, + CoercedValueIsSqlType = 16, + CoercedValueIsDataFeed = 32, + HasReceivedMetadata = 64, + ForceColumnEncryption = 128, + IsDerivedParameterTypeName = 256, + HasScale = 512, + } + private MetaType _metaType; private SqlCollation _collation; private SqlMetaDataXmlSchemaCollection _xmlSchemaCollection; @@ -217,14 +233,9 @@ private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) private string _parameterName; private byte _precision; private byte _scale; - private bool _hasScale; // V1.0 compat, ignore _hasScale private MetaType _internalMetaType; private SqlBuffer _sqlBufferReturnValue; private INullable _valueAsINullable; - private bool _isSqlParameterSqlType; - private bool _isNull; - private bool _coercedValueIsSqlType; - private bool _coercedValueIsDataFeed; private int _actualSize; private object _value; private object _coercedValue; @@ -234,13 +245,12 @@ private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) private int _offset; private string _sourceColumn; private DataRowVersion _sourceVersion; - private bool _sourceColumnNullMapping; - private bool _isNullable; + private SqlParameterFlags _flags; /// public SqlParameter() : base() { - _isNull = true; + _flags = SqlParameterFlags.IsNull; _actualSize = -1; _direction = ParameterDirection.Input; } @@ -360,7 +370,11 @@ private SqlParameter(SqlParameter source) : this() /// For unencrypted parameters, the encryption metadata should still be sent (and will indicate /// that no encryption is needed). /// - internal bool HasReceivedMetadata { get; set; } + internal bool HasReceivedMetadata + { + get => HasFlag(SqlParameterFlags.HasReceivedMetadata); + set => SetFlag(SqlParameterFlags.HasReceivedMetadata, value); + } /// /// Returns the normalization rule version number as a byte @@ -432,7 +446,11 @@ public string XmlSchemaCollectionName DefaultValue(false), ResCategory("Data") ] - public bool ForceColumnEncryption { get; set; } + public bool ForceColumnEncryption + { + get => HasFlag(SqlParameterFlags.ForceColumnEncryption); + set => SetFlag(SqlParameterFlags.ForceColumnEncryption, value); + } /// public override DbType DbType @@ -547,11 +565,11 @@ internal byte ScaleInternal } set { - if (_scale != value || !_hasScale) + if (_scale != value || !HasFlag(SqlParameterFlags.HasScale)) { PropertyChanging(); _scale = value; - _hasScale = true; + SetFlag(SqlParameterFlags.HasScale, true); _actualSize = -1; // Invalidate actual size such that it is re-calculated } } @@ -714,8 +732,8 @@ public override object Value _sqlBufferReturnValue = null; _coercedValue = null; _valueAsINullable = _value as INullable; - _isSqlParameterSqlType = _valueAsINullable != null; - _isNull = (null == _value) || (_value == DBNull.Value) || (_isSqlParameterSqlType && _valueAsINullable.IsNull); + SetFlag(SqlParameterFlags.IsSqlParameterSqlType, _valueAsINullable != null); + SetFlag(SqlParameterFlags.IsNull, (null == _value) || (_value == DBNull.Value) || (HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && _valueAsINullable.IsNull)); _udtLoadError = null; _actualSize = -1; } @@ -752,8 +770,8 @@ public override ParameterDirection Direction /// public override bool IsNullable { - get => _isNullable; - set => _isNullable = value; + get => HasFlag(SqlParameterFlags.IsNullable); + set => SetFlag(SqlParameterFlags.IsNullable, value); } /// @@ -819,8 +837,8 @@ public override string SourceColumn /// public override bool SourceColumnNullMapping { - get => _sourceColumnNullMapping; - set => _sourceColumnNullMapping = value; + get => HasFlag(SqlParameterFlags.SourceColumnNullMapping); + set => SetFlag(SqlParameterFlags.SourceColumnNullMapping, value); } /// @@ -869,7 +887,7 @@ internal bool CoercedValueIsDataFeed GetCoercedValue(); } AssertCachedPropertiesAreValid(); - return _coercedValueIsDataFeed; + return HasFlag(SqlParameterFlags.CoercedValueIsDataFeed); } } @@ -882,7 +900,7 @@ internal bool CoercedValueIsSqlType GetCoercedValue(); } AssertCachedPropertiesAreValid(); - return _coercedValueIsSqlType; + return HasFlag(SqlParameterFlags.CoercedValueIsSqlType); } } @@ -895,6 +913,11 @@ internal SqlCollation Collation set => _collation = value; } + private bool HasFlag(SqlParameterFlags flag) + { + return (_flags & flag) != 0; + } + internal bool IsNull { get @@ -902,9 +925,9 @@ internal bool IsNull // NOTE: Udts can change their value any time if (_internalMetaType.SqlDbType == SqlDbType.Udt) { - _isNull = (_value == null) || (_value == DBNull.Value) || (_isSqlParameterSqlType && _valueAsINullable.IsNull); + SetFlag(SqlParameterFlags.IsNull, (_value == null) || (_value == DBNull.Value) || (HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && _valueAsINullable.IsNull)); } - return _isNull; + return HasFlag(SqlParameterFlags.IsNull); } } @@ -947,8 +970,8 @@ internal byte PrecisionInternal internal bool ParameterIsSqlType { - get => _isSqlParameterSqlType; - set => _isSqlParameterSqlType = value; + get => HasFlag(SqlParameterFlags.IsSqlParameterSqlType); + set => SetFlag(SqlParameterFlags.IsSqlParameterSqlType, value); } internal string ParameterNameFixed @@ -967,7 +990,11 @@ internal string ParameterNameFixed internal INullable ValueAsINullable => _valueAsINullable; - internal bool IsDerivedParameterTypeName { get; set; } + internal bool IsDerivedParameterTypeName + { + get => HasFlag(SqlParameterFlags.IsDerivedParameterTypeName); + set => SetFlag(SqlParameterFlags.IsDerivedParameterTypeName, value); + } private void CloneHelper(SqlParameter destination) { @@ -978,9 +1005,17 @@ private void CloneHelper(SqlParameter destination) destination._offset = _offset; destination._sourceColumn = _sourceColumn; destination._sourceVersion = _sourceVersion; - destination._sourceColumnNullMapping = _sourceColumnNullMapping; - destination._isNullable = _isNullable; - + destination._flags = _flags & ( + SqlParameterFlags.SourceColumnNullMapping | + SqlParameterFlags.IsNull | + SqlParameterFlags.IsNullable | + SqlParameterFlags.IsSqlParameterSqlType | + SqlParameterFlags.CoercedValueIsDataFeed | + SqlParameterFlags.CoercedValueIsSqlType | + SqlParameterFlags.ForceColumnEncryption | + SqlParameterFlags.IsDerivedParameterTypeName + // HasScale and HasReceivedMetadata deliberately omitted + ); destination._metaType = _metaType; destination._collation = _collation; if (_xmlSchemaCollection != null) @@ -990,20 +1025,14 @@ private void CloneHelper(SqlParameter destination) destination._udtTypeName = _udtTypeName; destination._typeName = _typeName; destination._udtLoadError = _udtLoadError; - destination._parameterName = _parameterName; destination._precision = _precision; destination._scale = _scale; destination._sqlBufferReturnValue = _sqlBufferReturnValue; - destination._isSqlParameterSqlType = _isSqlParameterSqlType; destination._internalMetaType = _internalMetaType; destination.CoercedValue = CoercedValue; // copy cached value reference because of XmlReader problem destination._valueAsINullable = _valueAsINullable; - destination._isNull = _isNull; - destination._coercedValueIsDataFeed = _coercedValueIsDataFeed; - destination._coercedValueIsSqlType = _coercedValueIsSqlType; destination._actualSize = _actualSize; - destination.ForceColumnEncryption = ForceColumnEncryption; } internal void CopyTo(SqlParameter destination) @@ -1035,12 +1064,12 @@ internal void FixStreamDataForNonPLP() { object value = GetCoercedValue(); AssertCachedPropertiesAreValid(); - if (!_coercedValueIsDataFeed) + if (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed)) { return; } - _coercedValueIsDataFeed = false; + SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, false); if (value is TextDataFeed textFeed) { @@ -1466,7 +1495,7 @@ internal int GetActualSize() case SqlDbType.NText: case SqlDbType.Xml: { - coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (StringSize(val, _coercedValueIsSqlType)) : 0; + coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; if (_actualSize == -1) @@ -1481,7 +1510,7 @@ internal int GetActualSize() case SqlDbType.Text: { // for these types, ActualSize is the num of chars, not actual bytes - since non-unicode chars are not always uniform size - coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (StringSize(val, _coercedValueIsSqlType)) : 0; + coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; if (_actualSize == -1) @@ -1494,7 +1523,7 @@ internal int GetActualSize() case SqlDbType.VarBinary: case SqlDbType.Image: case SqlDbType.Timestamp: - coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (BinarySize(val, _coercedValueIsSqlType)) : 0; + coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (BinarySize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); if (_actualSize == -1) @@ -1553,14 +1582,15 @@ internal object GetCoercedValue() { // No coercion is done for DataFeeds and Nulls _coercedValue = Value; - _coercedValueIsSqlType = _coercedValue != null && _isSqlParameterSqlType; // set to null for output parameters that keeps _isSqlParameterSqlType - _coercedValueIsDataFeed = isDataFeed; + SetFlag(SqlParameterFlags.CoercedValueIsSqlType, _coercedValue != null && HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); // set to null for output parameters that keeps _isSqlParameterSqlType + SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, isDataFeed); _actualSize = IsNull ? 0 : -1; } else { - _coercedValue = CoerceValue(Value, _internalMetaType, out _coercedValueIsDataFeed, out bool typeChanged); - _coercedValueIsSqlType = _isSqlParameterSqlType && (!typeChanged); // Type changed always results in a CLR type + _coercedValue = CoerceValue(Value, _internalMetaType, out bool coercedValueIsDataFeed, out bool typeChanged); + SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, coercedValueIsDataFeed); + SetFlag(SqlParameterFlags.CoercedValueIsSqlType, HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && (!typeChanged)); // Type changed always results in a CLR type _actualSize = -1; } } @@ -1758,7 +1788,7 @@ internal SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue peekAhe [Conditional("DEBUG")] internal void AssertCachedPropertiesAreValid() { - AssertPropertiesAreValid(_coercedValue, _coercedValueIsSqlType, _coercedValueIsDataFeed, IsNull); + AssertPropertiesAreValid(_coercedValue, HasFlag(SqlParameterFlags.CoercedValueIsSqlType), HasFlag(SqlParameterFlags.CoercedValueIsDataFeed), IsNull); } [Conditional("DEBUG")] @@ -1806,7 +1836,7 @@ private MetaType GetMetaTypeOnly() } else if (_sqlBufferReturnValue != null) { // value came back from the server - Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType(_isSqlParameterSqlType); + Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType(HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); if (valueType != null) { return MetaType.GetMetaTypeFromType(valueType); @@ -1849,13 +1879,18 @@ internal void SetSqlBuffer(SqlBuffer buff) _sqlBufferReturnValue = buff; _value = null; _coercedValue = null; - _isNull = _sqlBufferReturnValue.IsNull; - _coercedValueIsDataFeed = false; - _coercedValueIsSqlType = false; + SetFlag(SqlParameterFlags.IsNull, _sqlBufferReturnValue.IsNull); + SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, false); + SetFlag(SqlParameterFlags.CoercedValueIsSqlType, false); _udtLoadError = null; _actualSize = -1; } + private void SetFlag(SqlParameterFlags flag, bool value) + { + _flags = value ? _flags | flag : _flags & ~flag; + } + internal void SetUdtLoadError(Exception e) { _udtLoadError = e; @@ -1955,7 +1990,7 @@ internal MetaType ValidateTypeLengths() if ( (maxSizeInBytes > TdsEnums.TYPE_SIZE_LIMIT) || - _coercedValueIsDataFeed || + HasFlag(SqlParameterFlags.CoercedValueIsDataFeed) || (sizeInCharacters == -1) || (actualSizeInBytes == -1) ) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs index c0a2e54021..7cd0681784 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -191,6 +191,22 @@ private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) } } + [Flags] + private enum SqlParameterFlags : ushort + { + None = 0, + IsNull = 1, + IsNullable = 2, + IsSqlParameterSqlType = 4, + SourceColumnNullMapping = 8, + CoercedValueIsSqlType = 16, + CoercedValueIsDataFeed = 32, + HasReceivedMetadata = 64, + ForceColumnEncryption = 128, + IsDerivedParameterTypeName = 256, + HasScale = 512, + } + private MetaType _metaType; private SqlCollation _collation; private SqlMetaDataXmlSchemaCollection _xmlSchemaCollection; @@ -200,14 +216,9 @@ private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) private string _parameterName; private byte _precision; private byte _scale; - private bool _hasScale; // V1.0 compat, ignore _hasScale private MetaType _internalMetaType; private SqlBuffer _sqlBufferReturnValue; private INullable _valueAsINullable; - private bool _isSqlParameterSqlType; - private bool _isNull; - private bool _coercedValueIsSqlType; - private bool _coercedValueIsDataFeed; private int _actualSize; private object _value; private object _coercedValue; @@ -217,13 +228,12 @@ private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) private int _offset; private string _sourceColumn; private DataRowVersion _sourceVersion; - private bool _sourceColumnNullMapping; - private bool _isNullable; + private SqlParameterFlags _flags; /// public SqlParameter() : base() { - _isNull = true; + _flags = SqlParameterFlags.IsNull; _actualSize = -1; _direction = ParameterDirection.Input; } @@ -343,7 +353,11 @@ private SqlParameter(SqlParameter source) : this() /// For unencrypted parameters, the encryption metadata should still be sent (and will indicate /// that no encryption is needed). /// - internal bool HasReceivedMetadata { get; set; } + internal bool HasReceivedMetadata + { + get => HasFlag(SqlParameterFlags.HasReceivedMetadata); + set => SetFlag(SqlParameterFlags.HasReceivedMetadata, value); + } /// /// Returns the normalization rule version number as a byte @@ -415,7 +429,11 @@ public string XmlSchemaCollectionName DefaultValue(false), ResCategory("Data") ] - public bool ForceColumnEncryption { get; set; } + public bool ForceColumnEncryption + { + get => HasFlag(SqlParameterFlags.ForceColumnEncryption); + set => SetFlag(SqlParameterFlags.ForceColumnEncryption, value); + } /// public override DbType DbType @@ -530,11 +548,11 @@ internal byte ScaleInternal } set { - if (_scale != value || !_hasScale) + if (_scale != value || !HasFlag(SqlParameterFlags.HasScale)) { PropertyChanging(); _scale = value; - _hasScale = true; + SetFlag(SqlParameterFlags.HasScale, true); _actualSize = -1; // Invalidate actual size such that it is re-calculated } } @@ -697,8 +715,8 @@ public override object Value _sqlBufferReturnValue = null; _coercedValue = null; _valueAsINullable = _value as INullable; - _isSqlParameterSqlType = _valueAsINullable != null; - _isNull = (null == _value) || (_value == DBNull.Value) || (_isSqlParameterSqlType && _valueAsINullable.IsNull); + SetFlag(SqlParameterFlags.IsSqlParameterSqlType, _valueAsINullable != null); + SetFlag(SqlParameterFlags.IsNull, (null == _value) || (_value == DBNull.Value) || (HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && _valueAsINullable.IsNull)); _udtLoadError = null; _actualSize = -1; } @@ -735,8 +753,8 @@ public override ParameterDirection Direction /// public override bool IsNullable { - get => _isNullable; - set => _isNullable = value; + get => HasFlag(SqlParameterFlags.IsNullable); + set => SetFlag(SqlParameterFlags.IsNullable, value); } /// @@ -802,8 +820,8 @@ public override string SourceColumn /// public override bool SourceColumnNullMapping { - get => _sourceColumnNullMapping; - set => _sourceColumnNullMapping = value; + get => HasFlag(SqlParameterFlags.SourceColumnNullMapping); + set => SetFlag(SqlParameterFlags.SourceColumnNullMapping, value); } /// @@ -854,7 +872,7 @@ internal bool CoercedValueIsDataFeed GetCoercedValue(); } AssertCachedPropertiesAreValid(); - return _coercedValueIsDataFeed; + return HasFlag(SqlParameterFlags.CoercedValueIsDataFeed); } } @@ -867,7 +885,7 @@ internal bool CoercedValueIsSqlType GetCoercedValue(); } AssertCachedPropertiesAreValid(); - return _coercedValueIsSqlType; + return HasFlag(SqlParameterFlags.CoercedValueIsSqlType); } } @@ -880,6 +898,11 @@ internal SqlCollation Collation set => _collation = value; } + private bool HasFlag(SqlParameterFlags flag) + { + return (_flags & flag) != 0; + } + internal bool IsNull { get @@ -887,9 +910,9 @@ internal bool IsNull // NOTE: Udts can change their value any time if (_internalMetaType.SqlDbType == SqlDbType.Udt) { - _isNull = (_value == null) || (_value == DBNull.Value) || (_isSqlParameterSqlType && _valueAsINullable.IsNull); + SetFlag(SqlParameterFlags.IsNull, (_value == null) || (_value == DBNull.Value) || (HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && _valueAsINullable.IsNull)); } - return _isNull; + return HasFlag(SqlParameterFlags.IsNull); } } @@ -932,8 +955,8 @@ internal byte PrecisionInternal internal bool ParameterIsSqlType { - get => _isSqlParameterSqlType; - set => _isSqlParameterSqlType = value; + get => HasFlag(SqlParameterFlags.IsSqlParameterSqlType); + set => SetFlag(SqlParameterFlags.IsSqlParameterSqlType, value); } internal string ParameterNameFixed @@ -954,7 +977,11 @@ internal string ParameterNameFixed internal INullable ValueAsINullable => _valueAsINullable; - internal bool IsDerivedParameterTypeName { get; set; } + internal bool IsDerivedParameterTypeName + { + get => HasFlag(SqlParameterFlags.IsDerivedParameterTypeName); + set => SetFlag(SqlParameterFlags.IsDerivedParameterTypeName, value); + } private void CloneHelper(SqlParameter destination) { @@ -965,9 +992,17 @@ private void CloneHelper(SqlParameter destination) destination._offset = _offset; destination._sourceColumn = _sourceColumn; destination._sourceVersion = _sourceVersion; - destination._sourceColumnNullMapping = _sourceColumnNullMapping; - destination._isNullable = _isNullable; - + destination._flags = _flags & ( + SqlParameterFlags.SourceColumnNullMapping | + SqlParameterFlags.IsNull | + SqlParameterFlags.IsNullable | + SqlParameterFlags.IsSqlParameterSqlType | + SqlParameterFlags.CoercedValueIsDataFeed | + SqlParameterFlags.CoercedValueIsSqlType | + SqlParameterFlags.ForceColumnEncryption | + SqlParameterFlags.IsDerivedParameterTypeName + // HasScale and HasReceivedMetadata deliberately omitted + ); destination._metaType = _metaType; destination._collation = _collation; if (_xmlSchemaCollection != null) @@ -977,20 +1012,14 @@ private void CloneHelper(SqlParameter destination) destination._udtTypeName = _udtTypeName; destination._typeName = _typeName; destination._udtLoadError = _udtLoadError; - destination._parameterName = _parameterName; destination._precision = _precision; destination._scale = _scale; destination._sqlBufferReturnValue = _sqlBufferReturnValue; - destination._isSqlParameterSqlType = _isSqlParameterSqlType; destination._internalMetaType = _internalMetaType; destination.CoercedValue = CoercedValue; // copy cached value reference because of XmlReader problem destination._valueAsINullable = _valueAsINullable; - destination._isNull = _isNull; - destination._coercedValueIsDataFeed = _coercedValueIsDataFeed; - destination._coercedValueIsSqlType = _coercedValueIsSqlType; destination._actualSize = _actualSize; - destination.ForceColumnEncryption = ForceColumnEncryption; } internal void CopyTo(SqlParameter destination) @@ -1026,12 +1055,12 @@ internal void FixStreamDataForNonPLP() { object value = GetCoercedValue(); AssertCachedPropertiesAreValid(); - if (!_coercedValueIsDataFeed) + if (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed)) { return; } - _coercedValueIsDataFeed = false; + SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, false); if (value is TextDataFeed textFeed) { @@ -1461,7 +1490,7 @@ internal int GetActualSize() case SqlDbType.NText: case SqlDbType.Xml: { - coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (StringSize(val, _coercedValueIsSqlType)) : 0; + coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; if (_actualSize == -1) @@ -1476,7 +1505,7 @@ internal int GetActualSize() case SqlDbType.Text: { // for these types, ActualSize is the num of chars, not actual bytes - since non-unicode chars are not always uniform size - coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (StringSize(val, _coercedValueIsSqlType)) : 0; + coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; if (_actualSize == -1) @@ -1489,7 +1518,7 @@ internal int GetActualSize() case SqlDbType.VarBinary: case SqlDbType.Image: case SqlDbType.Timestamp: - coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (BinarySize(val, _coercedValueIsSqlType)) : 0; + coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (BinarySize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); if (_actualSize == -1) @@ -1549,14 +1578,15 @@ internal object GetCoercedValue() { // No coercion is done for DataFeeds and Nulls _coercedValue = Value; - _coercedValueIsSqlType = _coercedValue != null && _isSqlParameterSqlType; // set to null for output parameters that keeps _isSqlParameterSqlType - _coercedValueIsDataFeed = isDataFeed; + SetFlag(SqlParameterFlags.CoercedValueIsSqlType, _coercedValue != null && HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); // set to null for output parameters that keeps _isSqlParameterSqlType + SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, isDataFeed); _actualSize = IsNull ? 0 : -1; } else { - _coercedValue = CoerceValue(Value, _internalMetaType, out _coercedValueIsDataFeed, out bool typeChanged); - _coercedValueIsSqlType = _isSqlParameterSqlType && (!typeChanged); // Type changed always results in a CLR type + _coercedValue = CoerceValue(Value, _internalMetaType, out bool coercedValueIsDataFeed, out bool typeChanged); + SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, coercedValueIsDataFeed); + SetFlag(SqlParameterFlags.CoercedValueIsSqlType, HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && (!typeChanged)); // Type changed always results in a CLR type _actualSize = -1; } } @@ -1755,7 +1785,7 @@ internal SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue peekAhe [Conditional("DEBUG")] internal void AssertCachedPropertiesAreValid() { - AssertPropertiesAreValid(_coercedValue, _coercedValueIsSqlType, _coercedValueIsDataFeed, IsNull); + AssertPropertiesAreValid(_coercedValue, HasFlag(SqlParameterFlags.CoercedValueIsSqlType), HasFlag(SqlParameterFlags.CoercedValueIsDataFeed), IsNull); } [Conditional("DEBUG")] @@ -1803,7 +1833,7 @@ private MetaType GetMetaTypeOnly() } else if (_sqlBufferReturnValue != null) { // value came back from the server - Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType(_isSqlParameterSqlType); + Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType(HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); if (valueType != null) { return MetaType.GetMetaTypeFromType(valueType); @@ -1841,14 +1871,19 @@ private void PropertyTypeChanging() internal void ResetParent() => _parent = null; + private void SetFlag(SqlParameterFlags flag, bool value) + { + _flags = value ? _flags | flag : _flags & ~flag; + } + internal void SetSqlBuffer(SqlBuffer buff) { _sqlBufferReturnValue = buff; _value = null; _coercedValue = null; - _isNull = _sqlBufferReturnValue.IsNull; - _coercedValueIsDataFeed = false; - _coercedValueIsSqlType = false; + SetFlag(SqlParameterFlags.IsNull, _sqlBufferReturnValue.IsNull); + SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, false); + SetFlag(SqlParameterFlags.CoercedValueIsSqlType, false); _udtLoadError = null; _actualSize = -1; } @@ -1972,7 +2007,7 @@ internal MetaType ValidateTypeLengths(bool yukonOrNewer) if ( (maxSizeInBytes > TdsEnums.TYPE_SIZE_LIMIT) || - _coercedValueIsDataFeed || + HasFlag(SqlParameterFlags.CoercedValueIsDataFeed) || (sizeInCharacters == -1) || (actualSizeInBytes == -1) ) From c832bd0ad14288bf0278cd52e827a5284547d711 Mon Sep 17 00:00:00 2001 From: Wraith Date: Mon, 14 Jun 2021 23:58:30 +0100 Subject: [PATCH 73/87] Share SqlNormalizer (#1057) --- .../src/Microsoft.Data.SqlClient.csproj | 5 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../Data/SqlClient/Server/sqlnorm.cs | 656 ------------------ .../netfx/src/Resources/Strings.Designer.cs | 18 +- .../netfx/src/Resources/Strings.es.resx | 2 +- .../netfx/src/Resources/Strings.fr.resx | 2 +- .../netfx/src/Resources/Strings.it.resx | 2 +- .../netfx/src/Resources/Strings.ja.resx | 2 +- .../netfx/src/Resources/Strings.ko.resx | 2 +- .../netfx/src/Resources/Strings.pt-BR.resx | 2 +- .../netfx/src/Resources/Strings.ru.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hans.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hant.resx | 2 +- .../Data/SqlClient/Server/SqlNormalizer.cs} | 87 +-- 14 files changed, 72 insertions(+), 716 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/sqlnorm.cs rename src/Microsoft.Data.SqlClient/{netcore/src/Microsoft/Data/SqlClient/Server/SqlNorm.cs => src/Microsoft/Data/SqlClient/Server/SqlNormalizer.cs} (89%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index f4f09ef5ea..c5fa057a37 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -133,6 +133,9 @@ Microsoft\Data\SqlClient\Server\SqlMethodAttribute.cs + + Microsoft\Data\SqlClient\Server\SqlNormalizer.cs + Microsoft\Data\SqlClient\Server\SqlUserDefinedTypeAttribute.cs @@ -449,7 +452,7 @@ Microsoft\Data\SqlClient\EnclavePackage.cs - + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 61cc77db4f..4c675f8d8e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -371,6 +371,9 @@ Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs + + Microsoft\Data\SqlClient\Server\SqlNormalizer.cs + Microsoft\Data\SqlClient\Server\SmiRecordBuffer.cs @@ -544,7 +547,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/sqlnorm.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/sqlnorm.cs deleted file mode 100644 index 96573d7010..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/sqlnorm.cs +++ /dev/null @@ -1,656 +0,0 @@ -// 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. - -//devnote: perf optimization: consider changing the calls to Array.Reverse to inline unsafe code - -using System; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.Data.SqlClient.Server -{ - - // The class that holds the offset, field, and normalizer for - // a particular field. - internal sealed class FieldInfoEx : IComparable - { - internal readonly int offset; - internal readonly FieldInfo fieldInfo; - internal readonly Normalizer normalizer; - - internal FieldInfoEx(FieldInfo fi, int offset, Normalizer normalizer) - { - this.fieldInfo = fi; - this.offset = offset; - Debug.Assert(normalizer != null, "normalizer argument should not be null!"); - this.normalizer = normalizer; - } - - // Sort fields by field offsets. - public int CompareTo(object other) - { - FieldInfoEx otherF = other as FieldInfoEx; - if (otherF == null) - return -1; - return this.offset.CompareTo(otherF.offset); - } - } - - // The most complex normalizer, a udt normalizer - internal sealed class BinaryOrderedUdtNormalizer : Normalizer - { - internal readonly FieldInfoEx[] m_fieldsToNormalize; - private int m_size; - private byte[] m_PadBuffer; - internal readonly object NullInstance; - //a boolean that tells us if a udt is a "top-level" udt, - //i.e. one that does not require a null byte header. - private bool m_isTopLevelUdt; - - [System.Security.Permissions.ReflectionPermission(System.Security.Permissions.SecurityAction.Assert, MemberAccess = true)] - private FieldInfo[] GetFields(Type t) - { - return t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - } - - internal BinaryOrderedUdtNormalizer(Type t, bool isTopLevelUdt) - { - this.m_skipNormalize = false; - if (this.m_skipNormalize) - { - //if skipping normalization, dont write the null - //byte header for IsNull - this.m_isTopLevelUdt = true; - } - //top level udt logic is disabled until we decide - //what to do about nested udts - this.m_isTopLevelUdt = true; - // else - // this.m_isTopLevelUdt = isTopLevelUdt; - //get all the fields - - FieldInfo[] fields = GetFields(t); - - m_fieldsToNormalize = new FieldInfoEx[fields.Length]; - - int i = 0; - - foreach (FieldInfo fi in fields) - { - int offset = Marshal.OffsetOf(fi.DeclaringType, fi.Name).ToInt32(); - m_fieldsToNormalize[i++] = new FieldInfoEx(fi, offset, GetNormalizer(fi.FieldType)); - } - - //sort by offset - Array.Sort(m_fieldsToNormalize); - //if this is not a top-level udt, do setup for null values. - //null values need to compare less than all other values, - //so prefix a null byte indicator. - if (!this.m_isTopLevelUdt) - { - //get the null value for this type, special case for sql types, which - //have a null field - if (typeof(System.Data.SqlTypes.INullable).IsAssignableFrom(t)) - { - PropertyInfo pi = t.GetProperty("Null", - BindingFlags.Public | BindingFlags.Static); - if (pi == null || pi.PropertyType != t) - { - FieldInfo fi = t.GetField("Null", BindingFlags.Public | BindingFlags.Static); - if (fi == null || fi.FieldType != t) - throw new Exception("could not find Null field/property in nullable type " + t); - else - this.NullInstance = fi.GetValue(null); - } - else - { - this.NullInstance = pi.GetValue(null, null); - } - //create the padding buffer - this.m_PadBuffer = new byte[this.Size - 1]; - } - } - } - - internal bool IsNullable - { - get - { - return this.NullInstance != null; - } - } - - // Normalize the top-level udt - internal void NormalizeTopObject(object udt, Stream s) - { - Normalize(null, udt, s); - } - - // Denormalize a top-level udt and return it - internal object DeNormalizeTopObject(Type t, Stream s) - { - return DeNormalizeInternal(t, s); - } - - // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. - [MethodImpl(MethodImplOptions.NoInlining)] - private object DeNormalizeInternal(Type t, Stream s) - { - object result = null; - //if nullable and not the top object, read the null marker - if (!this.m_isTopLevelUdt && typeof(System.Data.SqlTypes.INullable).IsAssignableFrom(t)) - { - byte nullByte = (byte)s.ReadByte(); - if (nullByte == 0) - { - result = this.NullInstance; - s.Read(m_PadBuffer, 0, m_PadBuffer.Length); - return result; - } - } - if (result == null) - result = Activator.CreateInstance(t); - foreach (FieldInfoEx myField in m_fieldsToNormalize) - { - myField.normalizer.DeNormalize(myField.fieldInfo, result, s); - } - return result; - } - - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - // if (fi != null) - // Console.WriteLine("normalizing " + fi.FieldType + " pos " + s.Position); - object inner; - if (fi == null) - { - inner = obj; - } - else - { - inner = GetValue(fi, obj); - } - - //If nullable and not the top object, write a null indicator - System.Data.SqlTypes.INullable oNullable = inner as System.Data.SqlTypes.INullable; - if (oNullable != null && !this.m_isTopLevelUdt) - { - if (oNullable.IsNull) - { - s.WriteByte(0); - s.Write(m_PadBuffer, 0, m_PadBuffer.Length); - return; - } - else - { - s.WriteByte(1); - } - } - - foreach (FieldInfoEx myField in m_fieldsToNormalize) - { - myField.normalizer.Normalize(myField.fieldInfo, inner, s); - } - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - SetValue(fi, recvr, DeNormalizeInternal(fi.FieldType, s)); - } - - internal override int Size - { - get - { - if (m_size != 0) - return m_size; - if (this.IsNullable && !this.m_isTopLevelUdt) - m_size = 1; - foreach (FieldInfoEx myField in m_fieldsToNormalize) - { - m_size += myField.normalizer.Size; - } - return m_size; - } - } - } - - internal abstract class Normalizer - { - /* - protected internal static string GetString(byte[] array) - { - StringBuilder sb = new StringBuilder(); - //sb.Append("0x"); - foreach (byte b in array) - { - sb.Append(b.ToString("X2", CultureInfo.InvariantCulture)); - } - return sb.ToString(); - } - */ - - protected bool m_skipNormalize; - - /* - internal static bool IsByteOrderedUdt(Type t) - { - SqlUserDefinedTypeAttribute a = SerializationHelper.GetUdtAttribute(t); - return a.IsByteOrdered; - } - */ - - internal static Normalizer GetNormalizer(Type t) - { - Normalizer n = null; - if (t.IsPrimitive) - { - if (t == typeof(byte)) - n = new ByteNormalizer(); - else if (t == typeof(sbyte)) - n = new SByteNormalizer(); - else if (t == typeof(bool)) - n = new BooleanNormalizer(); - else if (t == typeof(short)) - n = new ShortNormalizer(); - else if (t == typeof(ushort)) - n = new UShortNormalizer(); - else if (t == typeof(int)) - n = new IntNormalizer(); - else if (t == typeof(uint)) - n = new UIntNormalizer(); - else if (t == typeof(float)) - n = new FloatNormalizer(); - else if (t == typeof(double)) - n = new DoubleNormalizer(); - else if (t == typeof(long)) - n = new LongNormalizer(); - else if (t == typeof(ulong)) - n = new ULongNormalizer(); - } - else if (t.IsValueType) - { - n = new BinaryOrderedUdtNormalizer(t, false); - } - if (n == null) - throw new Exception(StringsHelper.GetString(Strings.Sql_CanotCreateNormalizer, t.FullName)); - n.m_skipNormalize = false; - return n; - } - - internal abstract void Normalize(FieldInfo fi, object recvr, Stream s); - internal abstract void DeNormalize(FieldInfo fi, object recvr, Stream s); - - protected void FlipAllBits(byte[] b) - { - for (int i = 0; i < b.Length; i++) - b[i] = (byte)~b[i]; - } - - [System.Security.Permissions.ReflectionPermission(System.Security.Permissions.SecurityAction.Assert, MemberAccess = true)] - protected object GetValue(FieldInfo fi, object obj) - { - return fi.GetValue(obj); - } - - [System.Security.Permissions.ReflectionPermission(System.Security.Permissions.SecurityAction.Assert, MemberAccess = true)] - protected void SetValue(FieldInfo fi, object recvr, object value) - { - fi.SetValue(recvr, value); - } - - internal abstract int Size { get; } - } - - internal sealed class BooleanNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - bool b = (bool)GetValue(fi, obj); - // Console.WriteLine("normalized " + fi.FieldType + " " + fi.GetValue(obj) - // + " to " + (b?"01":"00") + " pos " + s.Position); - s.WriteByte((byte)(b ? 1 : 0)); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte b = (byte)s.ReadByte(); - SetValue(fi, recvr, b == 1); - } - - internal override int Size { get { return 1; } } - } - - // I could not find a simple way to convert a sbyte to a byte - // and vice versa in the framework api. Convert.ToSByte() checks that - // the value is in range. - // So, we just do the conversion inline. - internal sealed class SByteNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - sbyte sb = (sbyte)GetValue(fi, obj); - byte b; - unchecked - { - b = (byte)sb; - } - if (!this.m_skipNormalize) - b ^= 0x80; //flip the sign bit - s.WriteByte(b); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte b = (byte)s.ReadByte(); - if (!this.m_skipNormalize) - b ^= 0x80; //flip the sign bit - sbyte sb; - unchecked - { - sb = (sbyte)b; - } - SetValue(fi, recvr, sb); - } - - internal override int Size { get { return 1; } } - } - - internal sealed class ByteNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - byte b = (byte)GetValue(fi, obj); - s.WriteByte(b); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte b = (byte)s.ReadByte(); - SetValue(fi, recvr, b); - } - - internal override int Size { get { return 1; } } - } - - internal sealed class ShortNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - byte[] b = BitConverter.GetBytes((short)GetValue(fi, obj)); - if (!m_skipNormalize) - { - Array.Reverse(b); - b[0] ^= 0x80; - } - s.Write(b, 0, b.Length); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte[] b = new Byte[2]; - s.Read(b, 0, b.Length); - if (!m_skipNormalize) - { - b[0] ^= 0x80; - Array.Reverse(b); - } - SetValue(fi, recvr, BitConverter.ToInt16(b, 0)); - } - - internal override int Size { get { return 2; } } - } - - internal sealed class UShortNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - byte[] b = BitConverter.GetBytes((ushort)GetValue(fi, obj)); - if (!m_skipNormalize) - { - Array.Reverse(b); - } - s.Write(b, 0, b.Length); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte[] b = new Byte[2]; - s.Read(b, 0, b.Length); - if (!m_skipNormalize) - { - Array.Reverse(b); - } - SetValue(fi, recvr, BitConverter.ToUInt16(b, 0)); - } - - internal override int Size { get { return 2; } } - } - - internal sealed class IntNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - byte[] b = BitConverter.GetBytes((int)GetValue(fi, obj)); - if (!m_skipNormalize) - { - Array.Reverse(b); - b[0] ^= 0x80; - } - // Console.WriteLine("normalized " + fi.FieldType + " " + fi.GetValue(obj) - // + " to " + GetString(b) + " pos " + s.Position); - s.Write(b, 0, b.Length); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte[] b = new Byte[4]; - s.Read(b, 0, b.Length); - if (!m_skipNormalize) - { - b[0] ^= 0x80; - Array.Reverse(b); - } - SetValue(fi, recvr, BitConverter.ToInt32(b, 0)); - } - - internal override int Size { get { return 4; } } - } - - internal sealed class UIntNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - byte[] b = BitConverter.GetBytes((uint)GetValue(fi, obj)); - if (!m_skipNormalize) - { - Array.Reverse(b); - } - s.Write(b, 0, b.Length); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte[] b = new byte[4]; - s.Read(b, 0, b.Length); - if (!m_skipNormalize) - { - Array.Reverse(b); - } - SetValue(fi, recvr, BitConverter.ToUInt32(b, 0)); - } - - internal override int Size { get { return 4; } } - } - - internal sealed class LongNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - byte[] b = BitConverter.GetBytes((long)GetValue(fi, obj)); - if (!m_skipNormalize) - { - Array.Reverse(b); - b[0] ^= 0x80; - } - s.Write(b, 0, b.Length); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte[] b = new Byte[8]; - s.Read(b, 0, b.Length); - if (!m_skipNormalize) - { - b[0] ^= 0x80; - Array.Reverse(b); - } - SetValue(fi, recvr, BitConverter.ToInt64(b, 0)); - } - - internal override int Size { get { return 8; } } - } - - internal sealed class ULongNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - byte[] b = BitConverter.GetBytes((ulong)GetValue(fi, obj)); - if (!m_skipNormalize) - { - Array.Reverse(b); - } - // Console.WriteLine("normalized " + fi.FieldType + " " + fi.GetValue(obj) - // + " to " + GetString(b)); - s.Write(b, 0, b.Length); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte[] b = new Byte[8]; - s.Read(b, 0, b.Length); - if (!m_skipNormalize) - { - Array.Reverse(b); - } - SetValue(fi, recvr, BitConverter.ToUInt64(b, 0)); - } - - internal override int Size { get { return 8; } } - } - - internal sealed class FloatNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - float f = (float)GetValue(fi, obj); - byte[] b = BitConverter.GetBytes(f); - if (!m_skipNormalize) - { - Array.Reverse(b); - if ((b[0] & 0x80) == 0) - { - // This is a positive number. - // Flip the highest bit - b[0] ^= 0x80; - } - else - { - // This is a negative number. - - // If all zeroes, means it was a negative zero. - // Treat it same as positive zero, so that - // the normalized key will compare equal. - if (f < 0) - FlipAllBits(b); - } - } - s.Write(b, 0, b.Length); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte[] b = new Byte[4]; - s.Read(b, 0, b.Length); - if (!m_skipNormalize) - { - if ((b[0] & 0x80) > 0) - { - // This is a positive number. - // Flip the highest bit - b[0] ^= 0x80; - } - else - { - // This is a negative number. - FlipAllBits(b); - } - Array.Reverse(b); - } - SetValue(fi, recvr, BitConverter.ToSingle(b, 0)); - } - - internal override int Size { get { return 4; } } - } - - internal sealed class DoubleNormalizer : Normalizer - { - internal override void Normalize(FieldInfo fi, object obj, Stream s) - { - double d = (double)GetValue(fi, obj); - byte[] b = BitConverter.GetBytes(d); - if (!m_skipNormalize) - { - Array.Reverse(b); - if ((b[0] & 0x80) == 0) - { - // This is a positive number. - // Flip the highest bit - b[0] ^= 0x80; - } - else - { - // This is a negative number. - if (d < 0) - { - // If all zeroes, means it was a negative zero. - // Treat it same as positive zero, so that - // the normalized key will compare equal. - FlipAllBits(b); - } - } - } - // Console.WriteLine("normalized " + fi.FieldType + " " + fi.GetValue(obj) - // + " to " + GetString(b)); - s.Write(b, 0, b.Length); - } - - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - byte[] b = new Byte[8]; - s.Read(b, 0, b.Length); - if (!m_skipNormalize) - { - if ((b[0] & 0x80) > 0) - { - // This is a positive number. - // Flip the highest bit - b[0] ^= 0x80; - } - else - { - // This is a negative number. - FlipAllBits(b); - } - Array.Reverse(b); - } - SetValue(fi, recvr, BitConverter.ToDouble(b, 0)); - } - - internal override int Size { get { return 8; } } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index e24b20f639..bf23167d40 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -9126,6 +9126,15 @@ internal class Strings { } } + /// + /// Looks up a localized string similar to Cannot create normalizer for '{0}'.. + /// + internal static string SQL_CannotCreateNormalizer { + get { + return ResourceManager.GetString("SQL_CannotCreateNormalizer", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot find an authentication provider for '{0}'.. /// @@ -9171,15 +9180,6 @@ internal class Strings { } } - /// - /// Looks up a localized string similar to Cannot create normalizer for '{0}'.. - /// - internal static string Sql_CanotCreateNormalizer { - get { - return ResourceManager.GetString("Sql_CanotCreateNormalizer", resourceCulture); - } - } - /// /// Looks up a localized string similar to Incorrect authentication parameters specified with certificate authentication.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index a448f5271f..fc01e8fddc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -3048,7 +3048,7 @@ La subclase no invalidó un método requerido. - + No se puede crear un normalizador para '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index b9538f5095..f1feaeb4dd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -3048,7 +3048,7 @@ La sous-classe n'a pas substituée une méthode requise. - + Impossible de créer un normaliseur pour '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 500eb618bb..879fedcc94 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -3048,7 +3048,7 @@ La sottoclasse non ha eseguito l'override di un metodo di richiesta. - + Impossibile creare un normalizzatore per '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index b24c73bc44..df391365d9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -3048,7 +3048,7 @@ サブクラスが、必要なメソッドをオーバーライドしませんでした。 - + '{0}' のノーマライザーを作成できません。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 4d630c1f02..9d273c6c79 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -3048,7 +3048,7 @@ 서브클래스에서 필요한 메서드를 재정의하지 않았습니다. - + '{0}'에 대한 노멀라이저를 만들 수 없습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index 109ae7bec2..b15d546517 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -3048,7 +3048,7 @@ A subclasse não substituiu um método necessário. - + Não é possível criar o normalizador para '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index fe96339d2d..5d0fdf79eb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -3048,7 +3048,7 @@ Подклассы не переопределяют необходимый метод. - + Не удается создать нормализатор для "{0}". diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 38fa7ec36f..61ac9d3945 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -3048,7 +3048,7 @@ 子类未覆盖所需方法。 - + 无法为“{0}”创建标准化程序。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 53fecc1fc7..cc14b49bfe 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -3048,7 +3048,7 @@ 子類別並未覆寫所需的方法。 - + 無法建立 '{0}' 的正規器。 diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlNorm.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlNormalizer.cs similarity index 89% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlNorm.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlNormalizer.cs index e0c24c87fb..e7dd37a955 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlNorm.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlNormalizer.cs @@ -20,24 +20,17 @@ internal sealed class FieldInfoEx : IComparable internal FieldInfoEx(FieldInfo fi, int offset, Normalizer normalizer) { + _offset = offset; + FieldInfo = fi; Debug.Assert(normalizer != null, "normalizer argument should not be null!"); Normalizer = normalizer; - FieldInfo = fi; - _offset = offset; } + internal FieldInfo FieldInfo { get; private set; } internal Normalizer Normalizer { get; private set; } // Sort fields by field offsets. - public int CompareTo(object other) - { - FieldInfoEx otherF = other as FieldInfoEx; - if (otherF == null) - { - return -1; - } - return _offset.CompareTo(otherF._offset); - } + public int CompareTo(object other) => other is FieldInfoEx otherEx ? _offset.CompareTo(otherEx._offset) : -1; } // The most complex normalizer, a udt normalizer @@ -45,12 +38,15 @@ internal sealed class BinaryOrderedUdtNormalizer : Normalizer { private readonly FieldInfoEx[] _fieldsToNormalize; private int _size; - private byte[] _padBuffer; - internal readonly object NullInstance; + private readonly byte[] _padBuffer; + private readonly object _nullInstance; //a boolean that tells us if a udt is a "top-level" udt, //i.e. one that does not require a null byte header. - private bool _isTopLevelUdt; + private readonly bool _isTopLevelUdt; +#if NETFRAMEWORK + [System.Security.Permissions.ReflectionPermission(System.Security.Permissions.SecurityAction.Assert, MemberAccess = true)] +#endif private FieldInfo[] GetFields(Type t) { return t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); @@ -66,9 +62,12 @@ internal BinaryOrderedUdtNormalizer(Type t, bool isTopLevelUdt) _isTopLevelUdt = true; } + // top level udt logic is disabled until we decide + // what to do about nested udts _isTopLevelUdt = true; + // else + // this._isTopLevelUdt = isTopLevelUdt; - // get all the fields FieldInfo[] fields = GetFields(t); _fieldsToNormalize = new FieldInfoEx[fields.Length]; @@ -98,27 +97,28 @@ internal BinaryOrderedUdtNormalizer(Type t, bool isTopLevelUdt) { FieldInfo fi = t.GetField("Null", BindingFlags.Public | BindingFlags.Static); if (fi == null || fi.FieldType != t) + { throw new Exception("could not find Null field/property in nullable type " + t); + } else - NullInstance = fi.GetValue(null); + { + _nullInstance = fi.GetValue(null); + } } else { - NullInstance = pi.GetValue(null, null); + _nullInstance = pi.GetValue(null, null); } - //create the padding buffer + _padBuffer = new byte[Size - 1]; } } } - internal bool IsNullable => (NullInstance != null); + internal bool IsNullable => _nullInstance != null; // Normalize the top-level udt - internal void NormalizeTopObject(object udt, Stream s) - { - Normalize(null, udt, s); - } + internal void NormalizeTopObject(object udt, Stream s) => Normalize(null, udt, s); // Denormalize a top-level udt and return it internal object DeNormalizeTopObject(Type t, Stream s) => DeNormalizeInternal(t, s); @@ -134,7 +134,7 @@ private object DeNormalizeInternal(Type t, Stream s) byte nullByte = (byte)s.ReadByte(); if (nullByte == 0) { - result = NullInstance; + result = _nullInstance; s.Read(_padBuffer, 0, _padBuffer.Length); return result; } @@ -143,9 +143,9 @@ private object DeNormalizeInternal(Type t, Stream s) { result = Activator.CreateInstance(t); } - foreach (FieldInfoEx myField in _fieldsToNormalize) + foreach (FieldInfoEx field in _fieldsToNormalize) { - myField.Normalizer.DeNormalize(myField.FieldInfo, result, s); + field.Normalizer.DeNormalize(field.FieldInfo, result, s); } return result; } @@ -163,9 +163,9 @@ internal override void Normalize(FieldInfo fi, object obj, Stream s) } // If nullable and not the top object, write a null indicator - if (inner is INullable oNullable && !_isTopLevelUdt) + if (inner is INullable nullable && !_isTopLevelUdt) { - if (oNullable.IsNull) + if (nullable.IsNull) { s.WriteByte(0); s.Write(_padBuffer, 0, _padBuffer.Length); @@ -177,16 +177,13 @@ internal override void Normalize(FieldInfo fi, object obj, Stream s) } } - foreach (FieldInfoEx myField in _fieldsToNormalize) + foreach (FieldInfoEx field in _fieldsToNormalize) { - myField.Normalizer.Normalize(myField.FieldInfo, inner, s); + field.Normalizer.Normalize(field.FieldInfo, inner, s); } } - internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) - { - SetValue(fi, recvr, DeNormalizeInternal(fi.FieldType, s)); - } + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) => SetValue(fi, recvr, DeNormalizeInternal(fi.FieldType, s)); internal override int Size { @@ -200,9 +197,9 @@ internal override int Size { _size = 1; } - foreach (FieldInfoEx myField in _fieldsToNormalize) + foreach (FieldInfoEx field in _fieldsToNormalize) { - _size += myField.Normalizer.Size; + _size += field.Normalizer.Size; } return _size; } @@ -264,9 +261,13 @@ protected void FlipAllBits(byte[] b) b[i] = (byte)~b[i]; } } - +#if NETFRAMEWORK + [System.Security.Permissions.ReflectionPermission(System.Security.Permissions.SecurityAction.Assert, MemberAccess = true)] +#endif protected object GetValue(FieldInfo fi, object obj) => fi.GetValue(obj); - +#if NETFRAMEWORK + [System.Security.Permissions.ReflectionPermission(System.Security.Permissions.SecurityAction.Assert, MemberAccess = true)] +#endif protected void SetValue(FieldInfo fi, object recvr, object value) => fi.SetValue(recvr, value); internal abstract int Size { get; } @@ -300,7 +301,9 @@ internal override void Normalize(FieldInfo fi, object obj, Stream s) b = (byte)sb; } if (!_skipNormalize) - b ^= 0x80; // flip the sign bit + { + b ^= 0x80; //flip the sign bit + } s.WriteByte(b); } @@ -308,7 +311,9 @@ internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) { byte b = (byte)s.ReadByte(); if (!_skipNormalize) - b ^= 0x80; // flip the sign bit + { + b ^= 0x80; //flip the sign bit + } sbyte sb; unchecked { @@ -522,7 +527,9 @@ internal override void Normalize(FieldInfo fi, object obj, Stream s) // Treat it same as positive zero, so that // the normalized key will compare equal. if (f < 0) + { FlipAllBits(b); + } } } s.Write(b, 0, b.Length); From 0a844fa0fcfaea8264c9c8a5951bad6c497f5d74 Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Tue, 15 Jun 2021 03:03:51 +0000 Subject: [PATCH 74/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.es.resx | 2 +- .../netfx/src/Resources/Strings.fr.resx | 2 +- .../netfx/src/Resources/Strings.it.resx | 2 +- .../netfx/src/Resources/Strings.ja.resx | 2 +- .../netfx/src/Resources/Strings.ko.resx | 2 +- .../netfx/src/Resources/Strings.pt-BR.resx | 2 +- .../netfx/src/Resources/Strings.ru.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hans.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hant.resx | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index fc01e8fddc..a448f5271f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -3048,7 +3048,7 @@ La subclase no invalidó un método requerido. - + No se puede crear un normalizador para '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index f1feaeb4dd..b9538f5095 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -3048,7 +3048,7 @@ La sous-classe n'a pas substituée une méthode requise. - + Impossible de créer un normaliseur pour '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 879fedcc94..500eb618bb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -3048,7 +3048,7 @@ La sottoclasse non ha eseguito l'override di un metodo di richiesta. - + Impossibile creare un normalizzatore per '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index df391365d9..b24c73bc44 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -3048,7 +3048,7 @@ サブクラスが、必要なメソッドをオーバーライドしませんでした。 - + '{0}' のノーマライザーを作成できません。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 9d273c6c79..4d630c1f02 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -3048,7 +3048,7 @@ 서브클래스에서 필요한 메서드를 재정의하지 않았습니다. - + '{0}'에 대한 노멀라이저를 만들 수 없습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index b15d546517..109ae7bec2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -3048,7 +3048,7 @@ A subclasse não substituiu um método necessário. - + Não é possível criar o normalizador para '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 5d0fdf79eb..fe96339d2d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -3048,7 +3048,7 @@ Подклассы не переопределяют необходимый метод. - + Не удается создать нормализатор для "{0}". diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 61ac9d3945..38fa7ec36f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -3048,7 +3048,7 @@ 子类未覆盖所需方法。 - + 无法为“{0}”创建标准化程序。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index cc14b49bfe..53fecc1fc7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -3048,7 +3048,7 @@ 子類別並未覆寫所需的方法。 - + 無法建立 '{0}' 的正規器。 From 94f401938ae2f5db20259d7037ca39aace57c02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 16 Jun 2021 09:22:30 +0200 Subject: [PATCH 75/87] Fix the version 3.0.0 release date in the release notes (#1119) --- release-notes/3.0/3.0.md | 2 +- release-notes/3.0/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/release-notes/3.0/3.0.md b/release-notes/3.0/3.0.md index 94dc6d0b24..178af0ad20 100644 --- a/release-notes/3.0/3.0.md +++ b/release-notes/3.0/3.0.md @@ -4,7 +4,7 @@ The following Microsoft.Data.SqlClient 3.0 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | -| 2020/06/09 | 3.0.0 | [release notes](3.0.0.md) | +| 2021/06/09 | 3.0.0 | [release notes](3.0.0.md) | The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: diff --git a/release-notes/3.0/README.md b/release-notes/3.0/README.md index 94dc6d0b24..178af0ad20 100644 --- a/release-notes/3.0/README.md +++ b/release-notes/3.0/README.md @@ -4,7 +4,7 @@ The following Microsoft.Data.SqlClient 3.0 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | -| 2020/06/09 | 3.0.0 | [release notes](3.0.0.md) | +| 2021/06/09 | 3.0.0 | [release notes](3.0.0.md) | The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: From d28fe2120df341b89bfe9c6cb48d2c988af5bba5 Mon Sep 17 00:00:00 2001 From: Wraith Date: Wed, 16 Jun 2021 23:50:18 +0100 Subject: [PATCH 76/87] Clean up AAsyncCallContext and SqlDataReader uses of it (#925) --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 339 ++++++++++-------- 1 file changed, 181 insertions(+), 158 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 a8a02be484..275fdde457 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 @@ -4343,16 +4343,16 @@ private static Task NextResultAsyncExecute(Task task, object state) if (task != null) { SqlClientEventSource.Log.TryTraceEvent("SqlDataReader.NextResultAsyncExecute | attempt retry {0}", ObjectID); - context._reader.PrepareForAsyncContinuation(); + context.Reader.PrepareForAsyncContinuation(); } - if (context._reader.TryNextResult(out bool more)) + if (context.Reader.TryNextResult(out bool more)) { // completed return more ? ADP.TrueTask : ADP.FalseTask; } - return context._reader.ExecuteAsyncCall(context); + return context.Reader.ExecuteAsyncCall(context); } // NOTE: This will return null if it completed sequentially @@ -4383,12 +4383,12 @@ internal Task GetBytesAsync(int columnIndex, byte[] buffer, int index, int var context = new GetBytesAsyncCallContext(this) { - columnIndex = columnIndex, - buffer = buffer, - index = index, - length = length, - timeout = timeout, - cancellationToken = cancellationToken, + _columnIndex = columnIndex, + _buffer = buffer, + _index = index, + _length = length, + _timeout = timeout, + _cancellationToken = cancellationToken, }; // Check if we need to skip columns @@ -4413,18 +4413,18 @@ internal Task GetBytesAsync(int columnIndex, byte[] buffer, int index, int timeoutToken = timeoutCancellationSource.Token; } - context._disposable = timeoutCancellationSource; - context.timeoutToken = timeoutToken; - context._source = source; PrepareAsyncInvocation(useSnapshot: true); + context.Set(this, source, timeoutCancellationSource); + context._timeoutToken = timeoutToken; + return InvokeAsyncCall(context); } else { // We're already at the correct column, just read the data - context.mode = GetBytesAsyncCallContext.OperationMode.Read; + context._mode = GetBytesAsyncCallContext.OperationMode.Read; // Switch to async PrepareAsyncInvocation(useSnapshot: false); @@ -4444,9 +4444,9 @@ internal Task GetBytesAsync(int columnIndex, byte[] buffer, int index, int private static Task GetBytesAsyncSeekExecute(Task task, object state) { GetBytesAsyncCallContext context = (GetBytesAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; - Debug.Assert(context.mode == GetBytesAsyncCallContext.OperationMode.Seek, "context.mode must be Seek to check if seeking can resume"); + Debug.Assert(context._mode == GetBytesAsyncCallContext.OperationMode.Seek, "context.mode must be Seek to check if seeking can resume"); if (task != null) { @@ -4456,16 +4456,16 @@ private static Task GetBytesAsyncSeekExecute(Task task, object state) // Prepare for stateObj timeout reader.SetTimeout(reader._defaultTimeoutMilliseconds); - if (reader.TryReadColumnHeader(context.columnIndex)) + if (reader.TryReadColumnHeader(context._columnIndex)) { // Only once we have read up to where we need to be can we check the cancellation tokens (otherwise we will be in an unknown state) - if (context.cancellationToken.IsCancellationRequested) + if (context._cancellationToken.IsCancellationRequested) { // User requested cancellation - return Task.FromCanceled(context.cancellationToken); + return Task.FromCanceled(context._cancellationToken); } - else if (context.timeoutToken.IsCancellationRequested) + else if (context._timeoutToken.IsCancellationRequested) { // Timeout return Task.FromException(ADP.ExceptionWithStackTrace(ADP.IO(SQLMessage.Timeout()))); @@ -4473,7 +4473,7 @@ private static Task GetBytesAsyncSeekExecute(Task task, object state) else { // Up to the correct column - continue to read - context.mode = GetBytesAsyncCallContext.OperationMode.Read; + context._mode = GetBytesAsyncCallContext.OperationMode.Read; reader.SwitchToAsyncWithoutSnapshot(); int totalBytesRead; var readTask = reader.GetBytesAsyncReadDataStage(context, true, out totalBytesRead); @@ -4497,18 +4497,18 @@ private static Task GetBytesAsyncSeekExecute(Task task, object state) private static Task GetBytesAsyncReadExecute(Task task, object state) { var context = (GetBytesAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; - Debug.Assert(context.mode == GetBytesAsyncCallContext.OperationMode.Read, "context.mode must be Read to check if read can resume"); + Debug.Assert(context._mode == GetBytesAsyncCallContext.OperationMode.Read, "context.mode must be Read to check if read can resume"); reader.PrepareForAsyncContinuation(); - if (context.cancellationToken.IsCancellationRequested) + if (context._cancellationToken.IsCancellationRequested) { // User requested cancellation - return Task.FromCanceled(context.cancellationToken); + return Task.FromCanceled(context._cancellationToken); } - else if (context.timeoutToken.IsCancellationRequested) + else if (context._timeoutToken.IsCancellationRequested) { // Timeout return Task.FromException(ADP.ExceptionWithStackTrace(ADP.IO(SQLMessage.Timeout()))); @@ -4520,18 +4520,18 @@ private static Task GetBytesAsyncReadExecute(Task task, object state) int bytesReadThisIteration; bool result = reader.TryGetBytesInternalSequential( - context.columnIndex, - context.buffer, - context.index + context.totalBytesRead, - context.length - context.totalBytesRead, + context._columnIndex, + context._buffer, + context._index + context._totalBytesRead, + context._length - context._totalBytesRead, out bytesReadThisIteration ); - context.totalBytesRead += bytesReadThisIteration; - Debug.Assert(context.totalBytesRead <= context.length, "Read more bytes than required"); + context._totalBytesRead += bytesReadThisIteration; + Debug.Assert(context._totalBytesRead <= context._length, "Read more bytes than required"); if (result) { - return Task.FromResult(context.totalBytesRead); + return Task.FromResult(context._totalBytesRead); } else { @@ -4542,24 +4542,24 @@ out bytesReadThisIteration private Task GetBytesAsyncReadDataStage(GetBytesAsyncCallContext context, bool isContinuation, out int bytesRead) { - Debug.Assert(context.mode == GetBytesAsyncCallContext.OperationMode.Read, "context.Mode must be Read to read data"); + Debug.Assert(context._mode == GetBytesAsyncCallContext.OperationMode.Read, "context.Mode must be Read to read data"); - _lastColumnWithDataChunkRead = context.columnIndex; + _lastColumnWithDataChunkRead = context._columnIndex; TaskCompletionSource source = null; // Prepare for stateObj timeout SetTimeout(_defaultTimeoutMilliseconds); // Try to read without any continuations (all the data may already be in the stateObj's buffer) - bool filledBuffer = context._reader.TryGetBytesInternalSequential( - context.columnIndex, - context.buffer, - context.index + context.totalBytesRead, - context.length - context.totalBytesRead, + bool filledBuffer = context.Reader.TryGetBytesInternalSequential( + context._columnIndex, + context._buffer, + context._index + context._totalBytesRead, + context._length - context._totalBytesRead, out bytesRead ); - context.totalBytesRead += bytesRead; - Debug.Assert(context.totalBytesRead <= context.length, "Read more bytes than required"); + context._totalBytesRead += bytesRead; + Debug.Assert(context._totalBytesRead <= context._length, "Read more bytes than required"); if (!filledBuffer) { @@ -4567,7 +4567,7 @@ out bytesRead if (!isContinuation) { // This is the first async operation which is happening - setup the _currentTask and timeout - Debug.Assert(context._source == null, "context._source should not be non-null when trying to change to async"); + Debug.Assert(context.Source == null, "context._source should not be non-null when trying to change to async"); source = new TaskCompletionSource(); Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); if (original != null) @@ -4575,7 +4575,7 @@ out bytesRead source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending())); return source.Task; } - context._source = source; + context.Source = source; // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) if (_cancelAsyncOnCloseToken.IsCancellationRequested) { @@ -4585,14 +4585,14 @@ out bytesRead } // Timeout - Debug.Assert(context.timeoutToken == CancellationToken.None, "TimeoutToken is set when GetBytesAsyncReadDataStage is not a continuation"); - if (context.timeout > 0) + Debug.Assert(context._timeoutToken == CancellationToken.None, "TimeoutToken is set when GetBytesAsyncReadDataStage is not a continuation"); + if (context._timeout > 0) { CancellationTokenSource timeoutCancellationSource = new CancellationTokenSource(); - timeoutCancellationSource.CancelAfter(context.timeout); - Debug.Assert(context._disposable is null, "setting context.disposable would lose the previous disposable"); - context._disposable = timeoutCancellationSource; - context.timeoutToken = timeoutCancellationSource.Token; + timeoutCancellationSource.CancelAfter(context._timeout); + Debug.Assert(context.Disposable is null, "setting context.disposable would lose the previous disposable"); + context.Disposable = timeoutCancellationSource; + context._timeoutToken = timeoutCancellationSource.Token; } } @@ -4604,10 +4604,10 @@ out bytesRead } else { - Debug.Assert(context._source != null, "context._source should not be null when continuing"); + Debug.Assert(context.Source != null, "context._source should not be null when continuing"); // setup for cleanup/completing retryTask.ContinueWith( - continuationAction: AAsyncCallContext.s_completeCallback, + continuationAction: SqlDataReaderAsyncCallContext.s_completeCallback, state: context, TaskScheduler.Default ); @@ -4749,7 +4749,7 @@ public override Task ReadAsync(CancellationToken cancellationToken) context = new ReadAsyncCallContext(); } - Debug.Assert(context._reader == null && context._source == null && context._disposable == null, "cached ReadAsyncCallContext was not properly disposed"); + Debug.Assert(context.Reader == null && context.Source == null && context.Disposable == null, "cached ReadAsyncCallContext was not properly disposed"); context.Set(this, source, registration); context._hasMoreData = more; @@ -4768,7 +4768,7 @@ public override Task ReadAsync(CancellationToken cancellationToken) private static Task ReadAsyncExecute(Task task, object state) { var context = (ReadAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; ref bool hasMoreData = ref context._hasMoreData; ref bool hasReadRowToken = ref context._hasReadRowToken; @@ -4927,7 +4927,7 @@ override public Task IsDBNullAsync(int i, CancellationToken cancellationTo context = new IsDBNullAsyncCallContext(); } - Debug.Assert(context._reader == null && context._source == null && context._disposable == null, "cached ISDBNullAsync context not properly disposed"); + Debug.Assert(context.Reader == null && context.Source == null && context.Disposable == null, "cached ISDBNullAsync context not properly disposed"); context.Set(this, source, registration); context._columnIndex = i; @@ -4942,7 +4942,7 @@ override public Task IsDBNullAsync(int i, CancellationToken cancellationTo private static Task IsDBNullAsyncExecute(Task task, object state) { IsDBNullAsyncCallContext context = (IsDBNullAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; if (task != null) { @@ -5021,7 +5021,7 @@ override public Task GetFieldValueAsync(int i, CancellationToken cancellat { _stateObj._shouldHaveEnoughData = true; #endif - return Task.FromResult(GetFieldValueInternal(i)); + return Task.FromResult(GetFieldValueInternal(i)); #if DEBUG } finally @@ -5061,19 +5061,22 @@ override public Task GetFieldValueAsync(int i, CancellationToken cancellat IDisposable registration = null; if (cancellationToken.CanBeCanceled) { - registration = cancellationToken.Register(s => ((SqlCommand)s).CancelIgnoreFailure(), _command); + registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command); } // Setup async PrepareAsyncInvocation(useSnapshot: true); - return InvokeAsyncCall(new GetFieldValueAsyncCallContext(this, source, registration, i)); + GetFieldValueAsyncCallContext context = new GetFieldValueAsyncCallContext(this, source, registration); + context._columnIndex = i; + + return InvokeAsyncCall(context); } private static Task GetFieldValueAsyncExecute(Task task, object state) { GetFieldValueAsyncCallContext context = (GetFieldValueAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; int columnIndex = context._columnIndex; if (task != null) { @@ -5112,71 +5115,48 @@ internal void CompletePendingReadWithFailure(int errorCode, bool resetForcePendi #endif - internal class Snapshot + internal abstract class SqlDataReaderAsyncCallContext : AAsyncCallContext { - public bool _dataReady; - public bool _haltRead; - public bool _metaDataConsumed; - public bool _browseModeInfoConsumed; - public bool _hasRows; - public ALTROWSTATUS _altRowStatus; - public int _nextColumnDataToRead; - public int _nextColumnHeaderToRead; - public long _columnDataBytesRead; - public long _columnDataBytesRemaining; + internal static readonly Action, object> s_completeCallback = CompleteAsyncCallCallback; - public _SqlMetaDataSet _metadata; - public _SqlMetaDataSetCollection _altMetaDataSetCollection; - public MultiPartTableName[] _tableNames; + internal static readonly Func> s_executeCallback = ExecuteAsyncCallCallback; - public SqlSequentialStream _currentStream; - public SqlSequentialTextReader _currentTextReader; - } - - internal abstract class AAsyncCallContext : IDisposable - { - internal static readonly Action, object> s_completeCallback = SqlDataReader.CompleteAsyncCallCallback; - - internal static readonly Func> s_executeCallback = SqlDataReader.ExecuteAsyncCallCallback; - - internal SqlDataReader _reader; - internal TaskCompletionSource _source; - internal IDisposable _disposable; - - protected AAsyncCallContext() + protected SqlDataReaderAsyncCallContext() { } - protected AAsyncCallContext(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable = null) + protected SqlDataReaderAsyncCallContext(SqlDataReader owner, TaskCompletionSource source, IDisposable disposable = null) { - Set(reader, source, disposable); + Set(owner, source, disposable); } - internal void Set(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable = null) + internal abstract Func> Execute { get; } + + internal SqlDataReader Reader { get => _owner; set => _owner = value; } + + public IDisposable Disposable { get => _disposable; set => _disposable = value; } + + public TaskCompletionSource Source { get => _source; set => _source = value; } + + new public void Set(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable) { - this._reader = reader ?? throw new ArgumentNullException(nameof(reader)); - this._source = source ?? throw new ArgumentNullException(nameof(source)); - this._disposable = disposable; + base.Set(reader, source, disposable); } - internal void Clear() + private static Task ExecuteAsyncCallCallback(Task task, object state) { - _source = null; - _reader = null; - IDisposable copyDisposable = _disposable; - _disposable = null; - copyDisposable?.Dispose(); + SqlDataReaderAsyncCallContext context = (SqlDataReaderAsyncCallContext)state; + return context.Reader.ContinueAsyncCall(task, context); } - internal abstract Func> Execute { get; } - - public virtual void Dispose() + private static void CompleteAsyncCallCallback(Task task, object state) { - Clear(); + SqlDataReaderAsyncCallContext context = (SqlDataReaderAsyncCallContext)state; + context.Reader.CompleteAsyncCall(task, context); } } - internal sealed class ReadAsyncCallContext : AAsyncCallContext + internal sealed class ReadAsyncCallContext : SqlDataReaderAsyncCallContext { internal static readonly Func> s_execute = SqlDataReader.ReadAsyncExecute; @@ -5189,15 +5169,13 @@ internal ReadAsyncCallContext() internal override Func> Execute => s_execute; - public override void Dispose() + protected override void AfterCleared(SqlDataReader owner) { - SqlDataReader reader = this._reader; - base.Dispose(); - reader.SetCachedReadAsyncCallContext(this); + owner.SetCachedReadAsyncCallContext(this); } } - internal sealed class IsDBNullAsyncCallContext : AAsyncCallContext + internal sealed class IsDBNullAsyncCallContext : SqlDataReaderAsyncCallContext { internal static readonly Func> s_execute = SqlDataReader.IsDBNullAsyncExecute; @@ -5207,15 +5185,13 @@ internal sealed class IsDBNullAsyncCallContext : AAsyncCallContext internal override Func> Execute => s_execute; - public override void Dispose() + protected override void AfterCleared(SqlDataReader owner) { - SqlDataReader reader = this._reader; - base.Dispose(); - reader.SetCachedIDBNullAsyncCallContext(this); + owner.SetCachedIDBNullAsyncCallContext(this); } } - private sealed class HasNextResultAsyncCallContext : AAsyncCallContext + private sealed class HasNextResultAsyncCallContext : SqlDataReaderAsyncCallContext { private static readonly Func> s_execute = SqlDataReader.NextResultAsyncExecute; @@ -5227,7 +5203,7 @@ public HasNextResultAsyncCallContext(SqlDataReader reader, TaskCompletionSource< internal override Func> Execute => s_execute; } - private sealed class GetBytesAsyncCallContext : AAsyncCallContext + private sealed class GetBytesAsyncCallContext : SqlDataReaderAsyncCallContext { internal enum OperationMode { @@ -5238,63 +5214,66 @@ internal enum OperationMode private static readonly Func> s_executeSeek = SqlDataReader.GetBytesAsyncSeekExecute; private static readonly Func> s_executeRead = SqlDataReader.GetBytesAsyncReadExecute; - internal int columnIndex; - internal byte[] buffer; - internal int index; - internal int length; - internal int timeout; - internal CancellationToken cancellationToken; - internal CancellationToken timeoutToken; - internal int totalBytesRead; + internal int _columnIndex; + internal byte[] _buffer; + internal int _index; + internal int _length; + internal int _timeout; + internal CancellationToken _cancellationToken; + internal CancellationToken _timeoutToken; + internal int _totalBytesRead; - internal OperationMode mode; + internal OperationMode _mode; internal GetBytesAsyncCallContext(SqlDataReader reader) { - this._reader = reader ?? throw new ArgumentNullException(nameof(reader)); + Reader = reader ?? throw new ArgumentNullException(nameof(reader)); } - internal override Func> Execute => mode == OperationMode.Seek ? s_executeSeek : s_executeRead; + internal override Func> Execute => _mode == OperationMode.Seek ? s_executeSeek : s_executeRead; - public override void Dispose() + protected override void Clear() { - buffer = null; - cancellationToken = default; - timeoutToken = default; - base.Dispose(); + _buffer = null; + _cancellationToken = default; + _timeoutToken = default; + base.Clear(); } } - private sealed class GetFieldValueAsyncCallContext : AAsyncCallContext + private sealed class GetFieldValueAsyncCallContext : SqlDataReaderAsyncCallContext { private static readonly Func> s_execute = SqlDataReader.GetFieldValueAsyncExecute; - internal readonly int _columnIndex; + internal int _columnIndex; + + internal GetFieldValueAsyncCallContext() { } - internal GetFieldValueAsyncCallContext(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable, int columnIndex) + internal GetFieldValueAsyncCallContext(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable) : base(reader, source, disposable) { - _columnIndex = columnIndex; } - internal override Func> Execute => s_execute; - } - - private static Task ExecuteAsyncCallCallback(Task task, object state) - { - AAsyncCallContext context = (AAsyncCallContext)state; - return context._reader.ExecuteAsyncCall(task, context); - } + protected override void Clear() + { + _columnIndex = -1; + base.Clear(); + } - private static void CompleteAsyncCallCallback(Task task, object state) - { - AAsyncCallContext context = (AAsyncCallContext)state; - context._reader.CompleteAsyncCall(task, context); + internal override Func> Execute => s_execute; } - private Task InvokeAsyncCall(AAsyncCallContext context) + /// + /// Starts the process of executing an async call using an SqlDataReaderAsyncCallContext derived context object. + /// After this call the context lifetime is handled by BeginAsyncCall ContinueAsyncCall and CompleteAsyncCall AsyncCall methods + /// + /// + /// + /// + /// + private Task InvokeAsyncCall(SqlDataReaderAsyncCallContext context) { - TaskCompletionSource source = context._source; + TaskCompletionSource source = context.Source; try { Task task; @@ -5314,7 +5293,7 @@ private Task InvokeAsyncCall(AAsyncCallContext context) else { task.ContinueWith( - continuationAction: AAsyncCallContext.s_completeCallback, + continuationAction: SqlDataReaderAsyncCallContext.s_completeCallback, state: context, TaskScheduler.Default ); @@ -5333,7 +5312,13 @@ private Task InvokeAsyncCall(AAsyncCallContext context) return source.Task; } - private Task ExecuteAsyncCall(AAsyncCallContext context) + /// + /// Begins an async call checking for cancellation and then setting up the callback for when data is available + /// + /// + /// + /// + private Task ExecuteAsyncCall(SqlDataReaderAsyncCallContext context) { // _networkPacketTaskSource could be null if the connection was closed // while an async invocation was outstanding. @@ -5346,14 +5331,23 @@ private Task ExecuteAsyncCall(AAsyncCallContext context) else { return completionSource.Task.ContinueWith( - continuationFunction: AAsyncCallContext.s_executeCallback, + continuationFunction: SqlDataReaderAsyncCallContext.s_executeCallback, state: context, TaskScheduler.Default ).Unwrap(); } } - private Task ExecuteAsyncCall(Task task, AAsyncCallContext context) + /// + /// When data has become available for an async call it is woken and this method is called. + /// It will call the async execution func and if a Task is returned indicating more data + /// is needed it will wait until it is called again when more is available + /// + /// + /// + /// + /// + private Task ContinueAsyncCall(Task task, SqlDataReaderAsyncCallContext context) { // this function must be an instance function called from the static callback because otherwise a compiler error // is caused by accessing the _cancelAsyncOnCloseToken field of a MarshalByRefObject derived class @@ -5406,9 +5400,16 @@ private Task ExecuteAsyncCall(Task task, AAsyncCallContext context) return Task.FromException(ADP.ExceptionWithStackTrace(ADP.ClosedConnectionError())); } - private void CompleteAsyncCall(Task task, AAsyncCallContext context) + /// + /// When data has been successfully processed for an async call the async func will call this + /// function to set the result into the task and cleanup the async state ready for another call + /// + /// + /// + /// + private void CompleteAsyncCall(Task task, SqlDataReaderAsyncCallContext context) { - TaskCompletionSource source = context._source; + TaskCompletionSource source = context.Source; context.Dispose(); // If something has forced us to switch to SyncOverAsync mode while in an async task then we need to guarantee that we do the cleanup @@ -5435,6 +5436,28 @@ private void CompleteAsyncCall(Task task, AAsyncCallContext context) } } + + internal class Snapshot + { + public bool _dataReady; + public bool _haltRead; + public bool _metaDataConsumed; + public bool _browseModeInfoConsumed; + public bool _hasRows; + public ALTROWSTATUS _altRowStatus; + public int _nextColumnDataToRead; + public int _nextColumnHeaderToRead; + public long _columnDataBytesRead; + public long _columnDataBytesRemaining; + + public _SqlMetaDataSet _metadata; + public _SqlMetaDataSetCollection _altMetaDataSetCollection; + public MultiPartTableName[] _tableNames; + + public SqlSequentialStream _currentStream; + public SqlSequentialTextReader _currentTextReader; + } + private void PrepareAsyncInvocation(bool useSnapshot) { // if there is already a snapshot, then the previous async command @@ -5645,5 +5668,5 @@ private ReadOnlyCollection BuildColumnSchema() return new ReadOnlyCollection(columnSchema); } - }// SqlDataReader -}// namespace + } +} From 7e86e4e8d26eb1b95be3d5f14019c3ad366adfaf Mon Sep 17 00:00:00 2001 From: Wraith Date: Wed, 16 Jun 2021 23:50:43 +0100 Subject: [PATCH 77/87] Sync SqlDataRecord (#1024) --- .../SqlDataRecord.xml | 2 +- .../Data/SqlClient/Server/SqlDataRecord.cs | 78 ++--- .../Data/SqlClient/Server/SqlDataRecord.cs | 323 +++++++++--------- 3 files changed, 202 insertions(+), 201 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlDataRecord.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlDataRecord.xml index cb89fe35a0..a2481e9363 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlDataRecord.xml +++ b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlDataRecord.xml @@ -2,7 +2,7 @@ - Represents a single row of data and its metadata. This class cannot be inherited. + Represents a single row of data and its metadata. public virtual int FieldCount @@ -50,7 +53,7 @@ public virtual string GetDataTypeName(int ordinal) { EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); - if (SqlDbType.Udt == metaData.SqlDbType) + if (metaData.SqlDbType == SqlDbType.Udt) { return metaData.UdtTypeName; } @@ -64,10 +67,8 @@ public virtual string GetDataTypeName(int ordinal) public virtual Type GetFieldType(int ordinal) { EnsureSubclassOverride(); - { - SqlMetaData md = GetSqlMetaData(ordinal); - return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).ClassType; - } + SqlMetaData md = GetSqlMetaData(ordinal); + return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).ClassType; } /// @@ -75,20 +76,14 @@ public virtual object GetValue(int ordinal) { EnsureSubclassOverride(); SmiMetaData metaData = GetSmiMetaData(ordinal); - - return ValueUtilsSmi.GetValue200( - _eventSink, - _recordBuffer, - ordinal, - metaData - ); + return ValueUtilsSmi.GetValue200(_eventSink, _recordBuffer, ordinal, metaData); } /// public virtual int GetValues(object[] values) { EnsureSubclassOverride(); - if (null == values) + if (values == null) { throw ADP.ArgumentNull(nameof(values)); } @@ -158,7 +153,7 @@ public virtual byte GetByte(int ordinal) public virtual long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) { EnsureSubclassOverride(); - return ValueUtilsSmi.GetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length, true); + return ValueUtilsSmi.GetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length, throwOnNull: true); } /// @@ -222,7 +217,7 @@ public virtual string GetString(int ordinal) { EnsureSubclassOverride(); SmiMetaData colMeta = GetSmiMetaData(ordinal); - if (_usesStringStorageForXml && SqlDbType.Xml == colMeta.SqlDbType) + if (_usesStringStorageForXml && colMeta.SqlDbType == SqlDbType.Xml) { return ValueUtilsSmi.GetString(_eventSink, _recordBuffer, ordinal, s_maxNVarCharForXml); } @@ -296,12 +291,11 @@ public virtual object GetSqlValue(int ordinal) public virtual int GetSqlValues(object[] values) { EnsureSubclassOverride(); - if (null == values) + if (values == null) { throw ADP.ArgumentNull(nameof(values)); } - int copyLength = (values.Length < FieldCount) ? values.Length : FieldCount; for (int i = 0; i < copyLength; i++) { @@ -428,7 +422,7 @@ public virtual SqlGuid GetSqlGuid(int ordinal) public virtual int SetValues(params object[] values) { EnsureSubclassOverride(); - if (null == values) + if (values == null) { throw ADP.ArgumentNull(nameof(values)); } @@ -443,8 +437,12 @@ public virtual int SetValues(params object[] values) { SqlMetaData metaData = GetSqlMetaData(i); typeCodes[i] = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( - metaData.SqlDbType, false /* isMultiValued */, values[i], metaData.Type); - if (ExtendedClrTypeCode.Invalid == typeCodes[i]) + metaData.SqlDbType, + isMultiValued: false, + values[i], + metaData.Type + ); + if (typeCodes[i] == ExtendedClrTypeCode.Invalid) { throw ADP.InvalidCast(); } @@ -454,7 +452,7 @@ public virtual int SetValues(params object[] values) // the validation loop and here, or if an invalid UDT was sent). for (int i = 0; i < copyLength; i++) { - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], 0, 0, null); + ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], offset: 0, length: 0, peekAhead: null); } return copyLength; @@ -466,13 +464,17 @@ public virtual void SetValue(int ordinal, object value) EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( - metaData.SqlDbType, false /* isMultiValued */, value, metaData.Type); - if (ExtendedClrTypeCode.Invalid == typeCode) + metaData.SqlDbType, + isMultiValued: false, + value, + metaData.Type + ); + if (typeCode == ExtendedClrTypeCode.Invalid) { throw ADP.InvalidCast(); } - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, 0, 0, null); + ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, offset: 0, length: 0, peekAhead: null); } /// @@ -550,6 +552,7 @@ public virtual void SetString(int ordinal, string value) EnsureSubclassOverride(); ValueUtilsSmi.SetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } + /// public virtual void SetDecimal(int ordinal, decimal value) { @@ -709,7 +712,7 @@ public virtual void SetSqlBytes(int ordinal, SqlBytes value) public SqlDataRecord(params SqlMetaData[] metaData) { // Initial consistency check - if (null == metaData) + if (metaData == null) { throw ADP.ArgumentNull(nameof(metaData)); } @@ -718,7 +721,7 @@ public SqlDataRecord(params SqlMetaData[] metaData) _columnSmiMetaData = new SmiExtendedMetaData[metaData.Length]; for (int i = 0; i < _columnSmiMetaData.Length; i++) { - if (null == metaData[i]) + if (metaData[i] == null) { throw ADP.ArgumentNull($"{nameof(metaData)}[{i}]"); } @@ -727,7 +730,6 @@ public SqlDataRecord(params SqlMetaData[] metaData) } _eventSink = new SmiEventSink_Default(); - _recordBuffer = new MemoryRecordBuffer(_columnSmiMetaData); _usesStringStorageForXml = true; _eventSink.ProcessMessagesAndThrow(); @@ -735,8 +737,8 @@ public SqlDataRecord(params SqlMetaData[] metaData) internal SqlDataRecord(SmiRecordBuffer recordBuffer, params SmiExtendedMetaData[] metaData) { - Debug.Assert(null != recordBuffer, "invalid attempt to instantiate SqlDataRecord with null SmiRecordBuffer"); - Debug.Assert(null != metaData, "invalid attempt to instantiate SqlDataRecord with null SmiExtendedMetaData[]"); + Debug.Assert(recordBuffer != null, "invalid attempt to instantiate SqlDataRecord with null SmiRecordBuffer"); + Debug.Assert(metaData != null, "invalid attempt to instantiate SqlDataRecord with null SmiExtendedMetaData[]"); _columnMetaData = new SqlMetaData[metaData.Length]; _columnSmiMetaData = new SmiExtendedMetaData[metaData.Length]; @@ -747,7 +749,6 @@ internal SqlDataRecord(SmiRecordBuffer recordBuffer, params SmiExtendedMetaData[ } _eventSink = new SmiEventSink_Default(); - _recordBuffer = recordBuffer; _eventSink.ProcessMessagesAndThrow(); } @@ -763,7 +764,6 @@ internal SmiRecordBuffer RecordBuffer } } - internal SqlMetaData[] InternalGetMetaData() { return _columnMetaData; @@ -786,15 +786,17 @@ internal void ThrowIfInvalidOrdinal(int ordinal) throw ADP.IndexOutOfRange(ordinal); } } + private void EnsureSubclassOverride() { - if (null == _recordBuffer) + if (_recordBuffer == null) { throw SQL.SubclassMustOverride(); } } /// + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] IDataReader System.Data.IDataRecord.GetData(int ordinal) { throw ADP.NotSupported(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs index 5ef71004c9..763469c004 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs @@ -11,25 +11,28 @@ namespace Microsoft.Data.SqlClient.Server { - /// + /// public class SqlDataRecord : IDataRecord { - SmiRecordBuffer _recordBuffer; - SmiContext _recordContext; - SmiExtendedMetaData[] _columnSmiMetaData; - SmiEventSink_Default _eventSink; - SqlMetaData[] _columnMetaData; - FieldNameLookup _fieldNameLookup; - bool _usesStringStorageForXml; - - static readonly SmiMetaData __maxNVarCharForXml = new SmiMetaData(SqlDbType.NVarChar, SmiMetaData.UnlimitedMaxLengthIndicator, - SmiMetaData.DefaultNVarChar_NoCollation.Precision, - SmiMetaData.DefaultNVarChar_NoCollation.Scale, - SmiMetaData.DefaultNVarChar.LocaleId, - SmiMetaData.DefaultNVarChar.CompareOptions, - null); - - /// + private SmiRecordBuffer _recordBuffer; + private SmiContext _recordContext; + private SmiExtendedMetaData[] _columnSmiMetaData; + private SmiEventSink_Default _eventSink; + private SqlMetaData[] _columnMetaData; + private FieldNameLookup _fieldNameLookup; + private bool _usesStringStorageForXml; + + static readonly SmiMetaData s_maxNVarCharForXml = new SmiMetaData( + SqlDbType.NVarChar, + SmiMetaData.UnlimitedMaxLengthIndicator, + SmiMetaData.DefaultNVarChar_NoCollation.Precision, + SmiMetaData.DefaultNVarChar_NoCollation.Scale, + SmiMetaData.DefaultNVarChar.LocaleId, + SmiMetaData.DefaultNVarChar.CompareOptions, + userDefinedType: null + ); + + /// public virtual int FieldCount { get @@ -39,19 +42,19 @@ public virtual int FieldCount } } - /// - public virtual String GetName(int ordinal) + /// + public virtual string GetName(int ordinal) { EnsureSubclassOverride(); return GetSqlMetaData(ordinal).Name; } - /// - public virtual String GetDataTypeName(int ordinal) + /// + public virtual string GetDataTypeName(int ordinal) { EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); - if (SqlDbType.Udt == metaData.SqlDbType) + if (metaData.SqlDbType == SqlDbType.Udt) { return metaData.UdtTypeName; } @@ -61,11 +64,11 @@ public virtual String GetDataTypeName(int ordinal) } } - /// + /// public virtual Type GetFieldType(int ordinal) { EnsureSubclassOverride(); - if (SqlDbType.Udt == GetSqlMetaData(ordinal).SqlDbType) + if (GetSqlMetaData(ordinal).SqlDbType == SqlDbType.Udt) { return GetSqlMetaData(ordinal).Type; } @@ -76,41 +79,29 @@ public virtual Type GetFieldType(int ordinal) } } - /// - public virtual Object GetValue(int ordinal) + /// + public virtual object GetValue(int ordinal) { EnsureSubclassOverride(); SmiMetaData metaData = GetSmiMetaData(ordinal); if (SmiVersion >= SmiContextFactory.KatmaiVersion) { - return ValueUtilsSmi.GetValue200( - _eventSink, - _recordBuffer, - ordinal, - metaData, - _recordContext - ); + return ValueUtilsSmi.GetValue200(_eventSink, _recordBuffer, ordinal, metaData, _recordContext); } else { - return ValueUtilsSmi.GetValue( - _eventSink, - (ITypedGettersV3)_recordBuffer, - ordinal, - metaData, - _recordContext - ); + return ValueUtilsSmi.GetValue(_eventSink, _recordBuffer, ordinal, metaData, _recordContext); } } - /// + /// public virtual int GetValues(object[] values) { EnsureSubclassOverride(); - if (null == values) + if (values == null) { - throw ADP.ArgumentNull("values"); + throw ADP.ArgumentNull(nameof(values)); } int copyLength = (values.Length < FieldCount) ? values.Length : FieldCount; @@ -122,7 +113,7 @@ public virtual int GetValues(object[] values) return copyLength; } - /// + /// public virtual int GetOrdinal(string name) { EnsureSubclassOverride(); @@ -134,13 +125,13 @@ public virtual int GetOrdinal(string name) names[i] = GetSqlMetaData(i).Name; } - _fieldNameLookup = new FieldNameLookup(names, -1); // UNDONE: is this correct LCID? + _fieldNameLookup = new FieldNameLookup(names, -1); } return _fieldNameLookup.GetOrdinal(name); } - /// + /// public virtual object this[int ordinal] { get @@ -150,8 +141,8 @@ public virtual int GetOrdinal(string name) } } - /// - public virtual object this[String name] + /// + public virtual object this[string name] { get { @@ -160,91 +151,91 @@ public virtual int GetOrdinal(string name) } } - /// + /// public virtual bool GetBoolean(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual byte GetByte(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) { EnsureSubclassOverride(); - return ValueUtilsSmi.GetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length, true); + return ValueUtilsSmi.GetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length, throwOnNull: true); } - /// + /// public virtual char GetChar(int ordinal) { EnsureSubclassOverride(); throw ADP.NotSupported(); } - /// + /// public virtual long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) { EnsureSubclassOverride(); return ValueUtilsSmi.GetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); } - /// + /// public virtual Guid GetGuid(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// - public virtual Int16 GetInt16(int ordinal) + /// + public virtual short GetInt16(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// - public virtual Int32 GetInt32(int ordinal) + /// + public virtual int GetInt32(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// - public virtual Int64 GetInt64(int ordinal) + /// + public virtual long GetInt64(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual float GetFloat(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual double GetDouble(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual string GetString(int ordinal) { EnsureSubclassOverride(); SmiMetaData colMeta = GetSmiMetaData(ordinal); - if (_usesStringStorageForXml && SqlDbType.Xml == colMeta.SqlDbType) + if (_usesStringStorageForXml && colMeta.SqlDbType == SqlDbType.Xml) { - return ValueUtilsSmi.GetString(_eventSink, _recordBuffer, ordinal, __maxNVarCharForXml); + return ValueUtilsSmi.GetString(_eventSink, _recordBuffer, ordinal, s_maxNVarCharForXml); } else { @@ -252,42 +243,35 @@ public virtual string GetString(int ordinal) } } - /// - public virtual Decimal GetDecimal(int ordinal) + /// + public virtual decimal GetDecimal(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual DateTime GetDateTime(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual DateTimeOffset GetDateTimeOffset(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual TimeSpan GetTimeSpan(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // MDAC 69508 - IDataReader IDataRecord.GetData(int ordinal) - { - throw ADP.NotSupported(); - } - - /// + /// public virtual bool IsDBNull(int ordinal) { EnsureSubclassOverride(); @@ -295,7 +279,7 @@ public virtual bool IsDBNull(int ordinal) return ValueUtilsSmi.IsDBNull(_eventSink, _recordBuffer, ordinal); } - /// + /// // ISqlRecord implementation public virtual SqlMetaData GetSqlMetaData(int ordinal) { @@ -303,7 +287,7 @@ public virtual SqlMetaData GetSqlMetaData(int ordinal) return _columnMetaData[ordinal]; } - /// + /// public virtual Type GetSqlFieldType(int ordinal) { EnsureSubclassOverride(); @@ -311,7 +295,7 @@ public virtual Type GetSqlFieldType(int ordinal) return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).SqlType; } - /// + /// public virtual object GetSqlValue(int ordinal) { EnsureSubclassOverride(); @@ -323,16 +307,15 @@ public virtual object GetSqlValue(int ordinal) return ValueUtilsSmi.GetSqlValue(_eventSink, _recordBuffer, ordinal, metaData, _recordContext); } - /// + /// public virtual int GetSqlValues(object[] values) { EnsureSubclassOverride(); - if (null == values) + if (values == null) { - throw ADP.ArgumentNull("values"); + throw ADP.ArgumentNull(nameof(values)); } - int copyLength = (values.Length < FieldCount) ? values.Length : FieldCount; for (int i = 0; i < copyLength; i++) { @@ -342,129 +325,129 @@ public virtual int GetSqlValues(object[] values) return copyLength; } - /// + /// public virtual SqlBinary GetSqlBinary(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlBytes GetSqlBytes(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); } - /// + /// public virtual SqlXml GetSqlXml(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); } - /// + /// public virtual SqlBoolean GetSqlBoolean(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlByte GetSqlByte(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlChars GetSqlChars(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); } - /// + /// public virtual SqlInt16 GetSqlInt16(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlInt32 GetSqlInt32(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlInt64 GetSqlInt64(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlSingle GetSqlSingle(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlDouble GetSqlDouble(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlMoney GetSqlMoney(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlDateTime GetSqlDateTime(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlDecimal GetSqlDecimal(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlString GetSqlString(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// public virtual SqlGuid GetSqlGuid(int ordinal) { EnsureSubclassOverride(); return ValueUtilsSmi.GetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); } - /// + /// // ISqlUpdateableRecord Implementation public virtual int SetValues(params object[] values) { EnsureSubclassOverride(); - if (null == values) + if (values == null) { - throw ADP.ArgumentNull("values"); + throw ADP.ArgumentNull(nameof(values)); } - // SQLBUDT #346883 Allow values array longer than FieldCount, just ignore the extra cells. + // Allow values array longer than FieldCount, just ignore the extra cells. int copyLength = (values.Length > FieldCount) ? FieldCount : values.Length; ExtendedClrTypeCode[] typeCodes = new ExtendedClrTypeCode[copyLength]; @@ -474,8 +457,13 @@ public virtual int SetValues(params object[] values) { SqlMetaData metaData = GetSqlMetaData(i); typeCodes[i] = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( - metaData.SqlDbType, false /* isMultiValued */, values[i], metaData.Type, SmiVersion); - if (ExtendedClrTypeCode.Invalid == typeCodes[i]) + metaData.SqlDbType, + isMultiValued: false, + values[i], + metaData.Type, + SmiVersion + ); + if (typeCodes[i] == ExtendedClrTypeCode.Invalid) { throw ADP.InvalidCast(); } @@ -487,264 +475,268 @@ public virtual int SetValues(params object[] values) { if (SmiVersion >= SmiContextFactory.KatmaiVersion) { - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], 0, 0, null); + ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], offset: 0, length: 0, peekAhead: null); } else { - ValueUtilsSmi.SetCompatibleValue(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], 0); + ValueUtilsSmi.SetCompatibleValue(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], offset: 0); } } return copyLength; } - /// + /// public virtual void SetValue(int ordinal, object value) { EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( - metaData.SqlDbType, false /* isMultiValued */, value, metaData.Type, SmiVersion); - if (ExtendedClrTypeCode.Invalid == typeCode) + metaData.SqlDbType, + isMultiValued:false, + value, + metaData.Type, + SmiVersion + ); + if (typeCode == ExtendedClrTypeCode.Invalid) { throw ADP.InvalidCast(); } if (SmiVersion >= SmiContextFactory.KatmaiVersion) { - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, 0, 0, null); + ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, offset: 0, length: 0, peekAhead: null); } else { - ValueUtilsSmi.SetCompatibleValue(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, 0); + ValueUtilsSmi.SetCompatibleValue(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, offset: 0); } } - /// + /// public virtual void SetBoolean(int ordinal, bool value) { EnsureSubclassOverride(); ValueUtilsSmi.SetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetByte(int ordinal, byte value) { EnsureSubclassOverride(); ValueUtilsSmi.SetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) { EnsureSubclassOverride(); ValueUtilsSmi.SetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); } - /// + /// public virtual void SetChar(int ordinal, char value) { EnsureSubclassOverride(); throw ADP.NotSupported(); } - /// + /// public virtual void SetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) { EnsureSubclassOverride(); ValueUtilsSmi.SetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); } - /// - public virtual void SetInt16(int ordinal, System.Int16 value) + /// + public virtual void SetInt16(int ordinal, short value) { EnsureSubclassOverride(); ValueUtilsSmi.SetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// - public virtual void SetInt32(int ordinal, System.Int32 value) + /// + public virtual void SetInt32(int ordinal, int value) { EnsureSubclassOverride(); ValueUtilsSmi.SetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// - public virtual void SetInt64(int ordinal, System.Int64 value) + /// + public virtual void SetInt64(int ordinal, long value) { EnsureSubclassOverride(); ValueUtilsSmi.SetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetFloat(int ordinal, float value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - - /// + /// public virtual void SetDouble(int ordinal, double value) { EnsureSubclassOverride(); ValueUtilsSmi.SetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetString(int ordinal, string value) { EnsureSubclassOverride(); ValueUtilsSmi.SetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// - public virtual void SetDecimal(int ordinal, Decimal value) + /// + public virtual void SetDecimal(int ordinal, decimal value) { EnsureSubclassOverride(); ValueUtilsSmi.SetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetDateTime(int ordinal, DateTime value) { EnsureSubclassOverride(); ValueUtilsSmi.SetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetTimeSpan(int ordinal, TimeSpan value) { EnsureSubclassOverride(); ValueUtilsSmi.SetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, SmiVersion >= SmiContextFactory.KatmaiVersion); } - /// + /// public virtual void SetDateTimeOffset(int ordinal, DateTimeOffset value) { EnsureSubclassOverride(); ValueUtilsSmi.SetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, SmiVersion >= SmiContextFactory.KatmaiVersion); } - /// + /// public virtual void SetDBNull(int ordinal) { EnsureSubclassOverride(); ValueUtilsSmi.SetDBNull(_eventSink, _recordBuffer, ordinal, true); } - /// + /// public virtual void SetGuid(int ordinal, Guid value) { EnsureSubclassOverride(); ValueUtilsSmi.SetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlBoolean(int ordinal, SqlBoolean value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlByte(int ordinal, SqlByte value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlInt16(int ordinal, SqlInt16 value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlInt32(int ordinal, SqlInt32 value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlInt64(int ordinal, SqlInt64 value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlSingle(int ordinal, SqlSingle value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlDouble(int ordinal, SqlDouble value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlMoney(int ordinal, SqlMoney value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlDateTime(int ordinal, SqlDateTime value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlXml(int ordinal, SqlXml value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlDecimal(int ordinal, SqlDecimal value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlString(int ordinal, SqlString value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlBinary(int ordinal, SqlBinary value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlGuid(int ordinal, SqlGuid value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlChars(int ordinal, SqlChars value) { EnsureSubclassOverride(); ValueUtilsSmi.SetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); } - /// + /// public virtual void SetSqlBytes(int ordinal, SqlBytes value) { EnsureSubclassOverride(); @@ -752,13 +744,13 @@ public virtual void SetSqlBytes(int ordinal, SqlBytes value) } // SqlDataRecord public API - /// + /// public SqlDataRecord(params SqlMetaData[] metaData) { // Initial consistency check - if (null == metaData) + if (metaData == null) { - throw ADP.ArgumentNull("metadata"); + throw ADP.ArgumentNull(nameof(metaData)); } _columnMetaData = new SqlMetaData[metaData.Length]; @@ -766,9 +758,9 @@ public SqlDataRecord(params SqlMetaData[] metaData) ulong smiVersion = SmiVersion; for (int i = 0; i < _columnSmiMetaData.Length; i++) { - if (null == metaData[i]) + if (metaData[i] == null) { - throw ADP.ArgumentNull("metadata[" + i + "]"); + throw ADP.ArgumentNull($"{nameof(metaData)}[{i}]"); } _columnMetaData[i] = metaData[i]; _columnSmiMetaData[i] = MetaDataUtilsSmi.SqlMetaDataToSmiExtendedMetaData(_columnMetaData[i]); @@ -797,8 +789,8 @@ public SqlDataRecord(params SqlMetaData[] metaData) internal SqlDataRecord(SmiRecordBuffer recordBuffer, params SmiExtendedMetaData[] metaData) { - Debug.Assert(null != recordBuffer, "invalid attempt to instantiate SqlDataRecord with null SmiRecordBuffer"); - Debug.Assert(null != metaData, "invalid attempt to instantiate SqlDataRecord with null SmiExtendedMetaData[]"); + Debug.Assert(recordBuffer != null, "invalid attempt to instantiate SqlDataRecord with null SmiRecordBuffer"); + Debug.Assert(metaData != null, "invalid attempt to instantiate SqlDataRecord with null SmiExtendedMetaData[]"); _columnMetaData = new SqlMetaData[metaData.Length]; _columnSmiMetaData = new SmiExtendedMetaData[metaData.Length]; @@ -809,7 +801,6 @@ internal SqlDataRecord(SmiRecordBuffer recordBuffer, params SmiExtendedMetaData[ } _eventSink = new SmiEventSink_Default(); - if (InOutOfProcHelper.InProc) { _recordContext = SmiContextFactory.Instance.GetCurrentContext(); @@ -871,13 +862,21 @@ internal void ThrowIfInvalidOrdinal(int ordinal) throw ADP.IndexOutOfRange(ordinal); } } + private void EnsureSubclassOverride() { - if (null == _recordBuffer) + if (_recordBuffer == null) { throw SQL.SubclassMustOverride(); } } + + /// + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + IDataReader System.Data.IDataRecord.GetData(int ordinal) + { + throw ADP.NotSupported(); + } } } From 0256b69452ecbcd200872589382bbf11087734f7 Mon Sep 17 00:00:00 2001 From: cmeyertons Date: Wed, 16 Jun 2021 17:54:32 -0500 Subject: [PATCH 78/87] SqlMetadataPrivFlags remove Enum.HasFlag in favor of bitwise operations (#1054) --- .../Data/SqlClient/TdsParserHelperClasses.cs | 30 ++++++++++++------- .../Data/SqlClient/TdsParserHelperClasses.cs | 30 ++++++++++++------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index 85327b3f97..6637517bc3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -427,6 +427,11 @@ internal _SqlMetaData(int ordinal) : base() this.ordinal = ordinal; } + private bool HasFlag(_SqlMetadataFlags flag) + { + return (flags & flag) != 0; + } + internal string serverName { get @@ -459,47 +464,47 @@ internal string tableName public byte Updatability { get => (byte)(flags & _SqlMetadataFlags.IsUpdatableMask); - set => flags = (_SqlMetadataFlags)((value & 0x3) | ((int)flags & ~0x03)); + set => flags = (_SqlMetadataFlags)((value & (byte)_SqlMetadataFlags.IsUpdatableMask) | ((int)flags & ~(byte)_SqlMetadataFlags.IsUpdatableMask)); } public bool IsReadOnly { - get => (flags & _SqlMetadataFlags.IsUpdatableMask) == 0; + get => !HasFlag(_SqlMetadataFlags.IsUpdatableMask); } public bool IsDifferentName { - get => flags.HasFlag(_SqlMetadataFlags.IsDifferentName); + get => HasFlag(_SqlMetadataFlags.IsDifferentName); set => Set(_SqlMetadataFlags.IsDifferentName, value); } public bool IsKey { - get => flags.HasFlag(_SqlMetadataFlags.IsKey); + get => HasFlag(_SqlMetadataFlags.IsKey); set => Set(_SqlMetadataFlags.IsKey, value); } public bool IsHidden { - get => flags.HasFlag(_SqlMetadataFlags.IsHidden); + get => HasFlag(_SqlMetadataFlags.IsHidden); set => Set(_SqlMetadataFlags.IsHidden, value); } public bool IsExpression { - get => flags.HasFlag(_SqlMetadataFlags.IsExpression); + get => HasFlag(_SqlMetadataFlags.IsExpression); set => Set(_SqlMetadataFlags.IsExpression, value); } public bool IsIdentity { - get => flags.HasFlag(_SqlMetadataFlags.IsIdentity); + get => HasFlag(_SqlMetadataFlags.IsIdentity); set => Set(_SqlMetadataFlags.IsIdentity, value); } public bool IsColumnSet { - get => flags.HasFlag(_SqlMetadataFlags.IsColumnSet); + get => HasFlag(_SqlMetadataFlags.IsColumnSet); set => Set(_SqlMetadataFlags.IsColumnSet, value); } @@ -746,16 +751,21 @@ internal SqlMetaDataPriv() public bool IsNullable { - get => flags.HasFlag(SqlMetaDataPrivFlags.IsNullable); + get => HasFlag(SqlMetaDataPrivFlags.IsNullable); set => Set(SqlMetaDataPrivFlags.IsNullable, value); } public bool IsMultiValued { - get => flags.HasFlag(SqlMetaDataPrivFlags.IsMultiValued); + get => HasFlag(SqlMetaDataPrivFlags.IsMultiValued); set => Set(SqlMetaDataPrivFlags.IsMultiValued, value); } + private bool HasFlag(SqlMetaDataPrivFlags flag) + { + return (flags & flag) != 0; + } + private void Set(SqlMetaDataPrivFlags flag, bool value) { flags = value ? flags | flag : flags & ~flag; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index 93e70f6ea1..2c0390476c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -602,6 +602,11 @@ internal _SqlMetaData(int ordinal) : base() this.ordinal = ordinal; } + private bool HasFlag(_SqlMetadataFlags flag) + { + return (flags & flag) != 0; + } + internal string serverName { get @@ -634,47 +639,47 @@ internal string tableName public byte Updatability { get => (byte)(flags & _SqlMetadataFlags.IsUpdatableMask); - set => flags = (_SqlMetadataFlags)((value & 0x3) | ((int)flags & ~0x03)); + set => flags = (_SqlMetadataFlags)((value & (byte)_SqlMetadataFlags.IsUpdatableMask) | ((int)flags & ~(byte)_SqlMetadataFlags.IsUpdatableMask)); } public bool IsReadOnly { - get => (flags & _SqlMetadataFlags.IsUpdatableMask) == 0; + get => !HasFlag(_SqlMetadataFlags.IsUpdatableMask); } public bool IsDifferentName { - get => flags.HasFlag(_SqlMetadataFlags.IsDifferentName); + get => HasFlag(_SqlMetadataFlags.IsDifferentName); set => Set(_SqlMetadataFlags.IsDifferentName, value); } public bool IsKey { - get => flags.HasFlag(_SqlMetadataFlags.IsKey); + get => HasFlag(_SqlMetadataFlags.IsKey); set => Set(_SqlMetadataFlags.IsKey, value); } public bool IsHidden { - get => flags.HasFlag(_SqlMetadataFlags.IsHidden); + get => HasFlag(_SqlMetadataFlags.IsHidden); set => Set(_SqlMetadataFlags.IsHidden, value); } public bool IsExpression { - get => flags.HasFlag(_SqlMetadataFlags.IsExpression); + get => HasFlag(_SqlMetadataFlags.IsExpression); set => Set(_SqlMetadataFlags.IsExpression, value); } public bool IsIdentity { - get => flags.HasFlag(_SqlMetadataFlags.IsIdentity); + get => HasFlag(_SqlMetadataFlags.IsIdentity); set => Set(_SqlMetadataFlags.IsIdentity, value); } public bool IsColumnSet { - get => flags.HasFlag(_SqlMetadataFlags.IsColumnSet); + get => HasFlag(_SqlMetadataFlags.IsColumnSet); set => Set(_SqlMetadataFlags.IsColumnSet, value); } @@ -1054,16 +1059,21 @@ internal SqlMetaDataPriv() public bool IsNullable { - get => flags.HasFlag(SqlMetaDataPrivFlags.IsNullable); + get => HasFlag(SqlMetaDataPrivFlags.IsNullable); set => Set(SqlMetaDataPrivFlags.IsNullable, value); } public bool IsMultiValued { - get => flags.HasFlag(SqlMetaDataPrivFlags.IsMultiValued); + get => HasFlag(SqlMetaDataPrivFlags.IsMultiValued); set => Set(SqlMetaDataPrivFlags.IsMultiValued, value); } + private bool HasFlag(SqlMetaDataPrivFlags flag) + { + return (flags & flag) != 0; + } + private void Set(SqlMetaDataPrivFlags flag, bool value) { flags = value ? flags | flag : flags & ~flag; From 355e1959bde167d70ecd3a1de9b963e76144746f Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Fri, 18 Jun 2021 18:24:45 -0700 Subject: [PATCH 79/87] update error list (#1125) Co-authored-by: Davoud Eshtehari --- .../Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs | 2 -- .../tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs index 280655c138..194a8aef41 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs @@ -57,8 +57,6 @@ public sealed class SqlConfigurableRetryFactory 10929, // Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. However, the server is currently too busy to support requests greater than %d for this database. For more information, see http://go.microsoft.com/fwlink/?LinkId=267637. Otherwise, please try again later. 10928, // Resource ID: %d. The %s limit for the database is %d and has been reached. For more information, see http://go.microsoft.com/fwlink/?LinkId=267637. 10060, // An error has occurred while establishing a connection to the server. When connecting to SQL Server, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: TCP Provider, error: 0 - A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.) (Microsoft SQL Server, Error: 10060) - 10054, // The data value for one or more columns overflowed the type used by the provider. - 10053, // Could not convert the data value due to reasons other than sign mismatch or overflow. 997, // A connection was successfully established with the server, but then an error occurred during the login process. (provider: Named Pipes Provider, error: 0 - Overlapped I/O operation is in progress) 233 // A connection was successfully established with the server, but then an error occurred during the login process. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.) (Microsoft SQL Server, Error: 233) }; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs index 5f5d1fe82e..40905a3bc0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs @@ -69,8 +69,6 @@ public class RetryLogicTestHelper 10929, // Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. However, the server is currently too busy to support requests greater than %d for this database. For more information, see http://go.microsoft.com/fwlink/?LinkId=267637. Otherwise, please try again later. 10928, // Resource ID: %d. The %s limit for the database is %d and has been reached. For more information, see http://go.microsoft.com/fwlink/?LinkId=267637. 10060, // An error has occurred while establishing a connection to the server. When connecting to SQL Server, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: TCP Provider, error: 0 - A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.) (Microsoft SQL Server, Error: 10060) - 10054, // The data value for one or more columns overflowed the type used by the provider. - 10053, // Could not convert the data value due to reasons other than sign mismatch or overflow. 997, // A connection was successfully established with the server, but then an error occurred during the login process. (provider: Named Pipes Provider, error: 0 - Overlapped I/O operation is in progress) 233, // A connection was successfully established with the server, but then an error occurred during the login process. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.) (Microsoft SQL Server, Error: 233) 64, From ddb7fc787b25639cab1fdccbb2f00dad5fe48d43 Mon Sep 17 00:00:00 2001 From: Wraith Date: Mon, 21 Jun 2021 18:50:41 +0100 Subject: [PATCH 80/87] Change WeakReference to WeakReference (#1122) --- .../Data/ProviderBase/DbConnectionInternal.cs | 36 +++++++++-------- .../Data/ProviderBase/DbConnectionInternal.cs | 4 +- .../Data/ProviderBase/DbConnectionInternal.cs | 39 ++++++++++--------- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index b24ed6810f..516aa9b7a9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -24,7 +24,7 @@ internal abstract partial class DbConnectionInternal private readonly bool _hidePassword; private readonly ConnectionState _state; - private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections) + private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections) private DbConnectionPool _connectionPool; // the pooler that the connection came from (Pooled connections only) private DbReferenceCollection _referenceCollection; // collection of objects that we need to notify in some way when we're being deactivated @@ -63,8 +63,7 @@ internal bool CanBePooled { get { - bool flag = (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.IsAlive); - return flag; + return (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.TryGetTarget(out DbConnection _)); } } @@ -103,8 +102,7 @@ internal bool IsEmancipated // of the pool and it's owning object is no longer around to // return it. - bool value = (_pooledCount < 1) && !_owningObject.IsAlive; - return value; + return (_pooledCount < 1) && !_owningObject.TryGetTarget(out DbConnection _); } } @@ -118,13 +116,17 @@ internal bool IsInPool } - protected internal object Owner + protected internal DbConnection Owner { // We use a weak reference to the owning object so we can identify when // it has been garbage collected without throwing exceptions. get { - return _owningObject.Target; + if (_owningObject.TryGetTarget(out DbConnection connection)) + { + return connection; + } + return null; } } @@ -276,13 +278,13 @@ protected internal virtual DataTable GetSchema(DbConnectionFactory factory, DbCo return metaDataFactory.GetSchema(outerConnection, collectionName, restrictions); } - internal void MakeNonPooledObject(object owningObject) + internal void MakeNonPooledObject(DbConnection owningObject) { // Used by DbConnectionFactory to indicate that this object IS NOT part of // a connection pool. _connectionPool = null; - _owningObject.Target = owningObject; + _owningObject.SetTarget(owningObject); _pooledCount = -1; } @@ -368,14 +370,15 @@ internal void PrePush(object expectedOwner) // ReclaimEmancipatedObjects. //3 // The following tests are retail assertions of things we can't allow to happen. - if (null == expectedOwner) + bool isAlive = _owningObject.TryGetTarget(out DbConnection connection); + if (expectedOwner == null) { - if (null != _owningObject.Target) + if (isAlive) { throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasOwner); // new unpooled object has an owner } } - else if (_owningObject.Target != expectedOwner) + else if (isAlive && connection != expectedOwner) { throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // unpooled object has incorrect owner } @@ -386,10 +389,10 @@ internal void PrePush(object expectedOwner) SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Preparing to push into pool, owning connection {1}, pooledCount={2}", ObjectID, 0, _pooledCount); _pooledCount++; - _owningObject.Target = null; // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2% + _owningObject.SetTarget(null); // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2% } - internal void PostPop(object newOwner) + internal void PostPop(DbConnection newOwner) { // Called by DbConnectionPool right after it pulls this from it's pool, we // take this opportunity to ensure ownership and pool counts are legit. @@ -406,12 +409,11 @@ internal void PostPop(object newOwner) // IMPORTANT NOTE: You must have taken a lock on the object before // you call this method to prevent race conditions with Clear and // ReclaimEmancipatedObjects. - - if (null != _owningObject.Target) + if (_owningObject.TryGetTarget(out DbConnection _)) { throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectHasOwner); // pooled connection already has an owner! } - _owningObject.Target = newOwner; + _owningObject.SetTarget(newOwner); _pooledCount--; SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Preparing to pop from pool, owning connection {1}, pooledCount={2}", ObjectID, 0, _pooledCount); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 790ad4d13d..8fc8df24e0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -306,7 +306,7 @@ internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFac // this is safe since we're about to dispose the // object and it won't have an owner after that for // certain. - _owningObject.Target = null; + _owningObject.SetTarget(null); if (IsTransactionRoot) { @@ -359,7 +359,7 @@ virtual internal void DelegatedTransactionEnded() } pool.PutObjectFromTransactedPool(this); } - else if (-1 == _pooledCount && !_owningObject.IsAlive) + else if (-1 == _pooledCount && !_owningObject.TryGetTarget(out DbConnection _)) { // When _pooledCount is -1 and the owning object no longer exists, // it indicates a closed (or leaked), non-pooled connection so diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 3256c9c39a..a79eb68411 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -31,7 +31,7 @@ internal abstract class DbConnectionInternal private readonly bool _hidePassword; private readonly ConnectionState _state; - private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections) + private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections) private DbConnectionPool _connectionPool; // the pooler that the connection came from (Pooled connections only) private DbConnectionPoolCounters _performanceCounters; // the performance counters we're supposed to update @@ -80,8 +80,7 @@ internal bool CanBePooled { get { - bool flag = (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.IsAlive); - return flag; + return (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.TryGetTarget(out DbConnection _)); } } @@ -288,8 +287,7 @@ internal bool IsEmancipated // of the pool and it's owning object is no longer around to // return it. - bool value = !IsTxRootWaitingForTxEnd && (_pooledCount < 1) && !_owningObject.IsAlive; - return value; + return !IsTxRootWaitingForTxEnd && (_pooledCount < 1) && !_owningObject.TryGetTarget(out DbConnection _); } } @@ -310,13 +308,17 @@ internal int ObjectID } } - protected internal object Owner + protected internal DbConnection Owner { // We use a weak reference to the owning object so we can identify when // it has been garbage collected without throwing exceptions. get { - return _owningObject.Target; + if (_owningObject.TryGetTarget(out DbConnection connection)) + { + return connection; + } + return null; } } @@ -508,7 +510,7 @@ internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFac // this is safe since we're about to dispose the // object and it won't have an owner after that for // certain. - _owningObject.Target = null; + _owningObject.SetTarget(null); if (IsTransactionRoot) { @@ -620,7 +622,7 @@ virtual internal void DelegatedTransactionEnded() } pool.PutObjectFromTransactedPool(this); } - else if (-1 == _pooledCount && !_owningObject.IsAlive) + else if (-1 == _pooledCount && !_owningObject.TryGetTarget(out DbConnection _)) { // When _pooledCount is -1 and the owning object no longer exists, // it indicates a closed (or leaked), non-pooled connection so @@ -692,14 +694,14 @@ virtual protected internal DataTable GetSchema(DbConnectionFactory factory, DbCo return metaDataFactory.GetSchema(outerConnection, collectionName, restrictions); } - internal void MakeNonPooledObject(object owningObject, DbConnectionPoolCounters performanceCounters) + internal void MakeNonPooledObject(DbConnection owningObject, DbConnectionPoolCounters performanceCounters) { // Used by DbConnectionFactory to indicate that this object IS NOT part of // a connection pool. _connectionPool = null; _performanceCounters = performanceCounters; - _owningObject.Target = owningObject; + _owningObject.SetTarget(owningObject); _pooledCount = -1; } @@ -788,14 +790,15 @@ internal void PrePush(object expectedOwner) // ReclaimEmancipatedObjects. //3 // The following tests are retail assertions of things we can't allow to happen. + bool isAlive = _owningObject.TryGetTarget(out DbConnection connection); if (null == expectedOwner) { - if (null != _owningObject.Target) + if (isAlive) { throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasOwner); // new unpooled object has an owner } } - else if (_owningObject.Target != expectedOwner) + else if (isAlive && connection != expectedOwner) { throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // unpooled object has incorrect owner } @@ -804,14 +807,13 @@ internal void PrePush(object expectedOwner) throw ADP.InternalError(ADP.InternalErrorCode.PushingObjectSecondTime); // pushing object onto stack a second time } - //DbConnection x = (expectedOwner as DbConnection); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Preparing to push into pool, owning connection {1}, pooledCount={2}", ObjectID, 0, _pooledCount); _pooledCount++; - _owningObject.Target = null; // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2% + _owningObject.SetTarget(null); // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2% } - internal void PostPop(object newOwner) + internal void PostPop(DbConnection newOwner) { // Called by DbConnectionPool right after it pulls this from it's pool, we // take this opportunity to ensure ownership and pool counts are legit. @@ -828,12 +830,11 @@ internal void PostPop(object newOwner) // IMPORTANT NOTE: You must have taken a lock on the object before // you call this method to prevent race conditions with Clear and // ReclaimEmancipatedObjects. - - if (null != _owningObject.Target) + if (_owningObject.TryGetTarget(out DbConnection _)) { throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectHasOwner); // pooled connection already has an owner! } - _owningObject.Target = newOwner; + _owningObject.SetTarget(newOwner); _pooledCount--; //DbConnection x = (newOwner as DbConnection); From 2891166cc6ee7843729a57a2fb0c036ef9ce1891 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 21 Jun 2021 10:51:13 -0700 Subject: [PATCH 81/87] 4.0 update (#1129) --- tools/props/Versions.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 3f667b8c65..12114ee14f 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -4,9 +4,9 @@ 1.0.0.0 - - 3.0.0.0 - 3.0.0-dev + + 4.0.0.0 + 4.0.0-dev $(NugetPackageVersion) From 95d23bb39ca74ffd0177e50c9613d4e90505758f Mon Sep 17 00:00:00 2001 From: Wraith Date: Wed, 23 Jun 2021 18:42:33 +0100 Subject: [PATCH 82/87] Update CannotCreateNormalize resource name in resource files (#1134) --- .../netfx/src/Resources/Strings.de.resx | 2 +- .../netfx/src/Resources/Strings.es.resx | 2 +- .../netfx/src/Resources/Strings.fr.resx | 2 +- .../netfx/src/Resources/Strings.it.resx | 2 +- .../netfx/src/Resources/Strings.ja.resx | 2 +- .../netfx/src/Resources/Strings.ko.resx | 2 +- .../netfx/src/Resources/Strings.pt-BR.resx | 2 +- src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx | 2 +- .../netfx/src/Resources/Strings.ru.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hans.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hant.resx | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 8e68b802c9..9d6a507ca1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -3048,7 +3048,7 @@ Die Unterklasse hat keine erforderliche Methode überschrieben. - + Die Normalisierungsfunktion für '{0}' kann nicht erstellt werden. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index a448f5271f..2f7aba82ec 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -3048,7 +3048,7 @@ La subclase no invalidó un método requerido. - + No se puede crear un normalizador para '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index b9538f5095..85243af3e7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -3048,7 +3048,7 @@ La sous-classe n'a pas substituée une méthode requise. - + Impossible de créer un normaliseur pour '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 500eb618bb..828639dab6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -3048,7 +3048,7 @@ La sottoclasse non ha eseguito l'override di un metodo di richiesta. - + Impossibile creare un normalizzatore per '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index b24c73bc44..fe44b01f9c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -3048,7 +3048,7 @@ サブクラスが、必要なメソッドをオーバーライドしませんでした。 - + '{0}' のノーマライザーを作成できません。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 4d630c1f02..83dbcb8745 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -3048,7 +3048,7 @@ 서브클래스에서 필요한 메서드를 재정의하지 않았습니다. - + '{0}'에 대한 노멀라이저를 만들 수 없습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index 109ae7bec2..d3a058aec0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -3048,7 +3048,7 @@ A subclasse não substituiu um método necessário. - + Não é possível criar o normalizador para '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 522f305696..c0ac110600 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -3048,7 +3048,7 @@ Subclass did not override a required method. - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index fe96339d2d..72e5d118ff 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -3048,7 +3048,7 @@ Подклассы не переопределяют необходимый метод. - + Не удается создать нормализатор для "{0}". diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 38fa7ec36f..360e488412 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -3048,7 +3048,7 @@ 子类未覆盖所需方法。 - + 无法为“{0}”创建标准化程序。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 53fecc1fc7..836b0cbd32 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -3048,7 +3048,7 @@ 子類別並未覆寫所需的方法。 - + 無法建立 '{0}' 的正規器。 From 1b79752c142da439290b6be2f4a07f28ed2393d9 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Wed, 23 Jun 2021 12:49:12 -0700 Subject: [PATCH 83/87] Add sample on how to register and reuse SqlColumnEncryptionAzureKeyVaultProvider across multiple SqlCommand objects (#1135) * Create RegisterCustomKeyStoreProvider_Example.cs * add using statements --- .../RegisterCustomKeyStoreProvider_Example.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 doc/samples/RegisterCustomKeyStoreProvider_Example.cs diff --git a/doc/samples/RegisterCustomKeyStoreProvider_Example.cs b/doc/samples/RegisterCustomKeyStoreProvider_Example.cs new file mode 100644 index 0000000000..6a7543a5cf --- /dev/null +++ b/doc/samples/RegisterCustomKeyStoreProvider_Example.cs @@ -0,0 +1,51 @@ +// +using Microsoft.Data.SqlClient; +using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; +using System.Collections.Generic; + +class Program +{ + // Links a SqlColumnEncryptionKeyStoreProvider to some object that represents a user + static Dictionary providerByUser = new(); + + void ExecuteSelectQuery(object user, SqlConnection connection) + { + // Check if the user already has a SqlColumnEncryptionAzureKeyVaultProvider + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = providerByUser[user]; + if (azureKeyVaultProvider is null) + { + // Create a new SqlColumnEncryptionAzureKeyVaultProvider with the user's credentials and save it for future use + azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + providerByUser[user] = azureKeyVaultProvider; + } + + Dictionary customProviders = new(); + customProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + + using SqlCommand command = new("SELECT * FROM Customers", connection); + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProviders); + // Perform database operations + // Any decrypted column encryption keys will be cached by azureKeyVaultProvider + } + + void ExecuteUpdateQuery(object user, SqlConnection connection) + { + // Check if the user already has a SqlColumnEncryptionAzureKeyVaultProvider + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = providerByUser[user]; + if (azureKeyVaultProvider is null) + { + // Create a new SqlColumnEncryptionAzureKeyVaultProvider with the user's credentials and save it for future use + azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + providerByUser[user] = azureKeyVaultProvider; + } + + Dictionary customProviders = new(); + customProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + + using SqlCommand command = new("UPDATE Customers SET Name = 'NewName' WHERE CustomerId = 1", connection); + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProviders); + // Perform database operations + // Any decrypted column encryption keys will be cached by azureKeyVaultProvider + } +} +// From 5c236a20198a229f390372a9e7c82fb17dace294 Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Thu, 24 Jun 2021 03:04:05 +0000 Subject: [PATCH 84/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 2 +- .../netfx/src/Resources/Strings.es.resx | 2 +- .../netfx/src/Resources/Strings.fr.resx | 2 +- .../netfx/src/Resources/Strings.it.resx | 2 +- .../netfx/src/Resources/Strings.ja.resx | 2 +- .../netfx/src/Resources/Strings.ko.resx | 2 +- .../netfx/src/Resources/Strings.pt-BR.resx | 2 +- .../netfx/src/Resources/Strings.ru.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hans.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hant.resx | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 9d6a507ca1..3e7fc72ca7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -3049,7 +3049,7 @@ Die Unterklasse hat keine erforderliche Methode überschrieben. - Die Normalisierungsfunktion für '{0}' kann nicht erstellt werden. + Cannot create normalizer for '{0}'. Interner Fehler diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index 2f7aba82ec..69a8f3a430 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -3049,7 +3049,7 @@ La subclase no invalidó un método requerido. - No se puede crear un normalizador para '{0}'. + Cannot create normalizer for '{0}'. Error interno diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index 85243af3e7..46f0ab2621 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -3049,7 +3049,7 @@ La sous-classe n'a pas substituée une méthode requise. - Impossible de créer un normaliseur pour '{0}'. + Cannot create normalizer for '{0}'. Erreur interne diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 828639dab6..458f318968 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -3049,7 +3049,7 @@ La sottoclasse non ha eseguito l'override di un metodo di richiesta. - Impossibile creare un normalizzatore per '{0}'. + Cannot create normalizer for '{0}'. Errore interno diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index fe44b01f9c..88a30ebd55 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -3049,7 +3049,7 @@ サブクラスが、必要なメソッドをオーバーライドしませんでした。 - '{0}' のノーマライザーを作成できません。 + Cannot create normalizer for '{0}'. 内部エラー diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 83dbcb8745..15eb33db16 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -3049,7 +3049,7 @@ 서브클래스에서 필요한 메서드를 재정의하지 않았습니다. - '{0}'에 대한 노멀라이저를 만들 수 없습니다. + Cannot create normalizer for '{0}'. 내부 오류 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index d3a058aec0..8a46736b4b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -3049,7 +3049,7 @@ A subclasse não substituiu um método necessário. - Não é possível criar o normalizador para '{0}'. + Cannot create normalizer for '{0}'. Erro Interno diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 72e5d118ff..c811c6e03f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -3049,7 +3049,7 @@ Подклассы не переопределяют необходимый метод. - Не удается создать нормализатор для "{0}". + Cannot create normalizer for '{0}'. Внутренняя ошибка diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 360e488412..a141a0054b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -3049,7 +3049,7 @@ 子类未覆盖所需方法。 - 无法为“{0}”创建标准化程序。 + Cannot create normalizer for '{0}'. 内部错误 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 836b0cbd32..9134b804a0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -3049,7 +3049,7 @@ 子類別並未覆寫所需的方法。 - 無法建立 '{0}' 的正規器。 + Cannot create normalizer for '{0}'. 內部錯誤 From 1bbafb9416856c1f3e18de628eda5dcbd47c5f6a Mon Sep 17 00:00:00 2001 From: Javad Date: Thu, 24 Jun 2021 16:35:00 -0700 Subject: [PATCH 85/87] Fix strings.resx (#1136) --- src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index c0ac110600..c11ae82496 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -3048,7 +3048,7 @@ Subclass did not override a required method. - + Cannot create normalizer for '{0}'. From a0a66a7546076f93f6dcfab825516dfcf6076ce8 Mon Sep 17 00:00:00 2001 From: Wraith Date: Fri, 25 Jun 2021 01:16:47 +0100 Subject: [PATCH 86/87] Cleanup SqlDataRecord (#1133) --- .../Data/SqlClient/Server/SqlDataRecord.cs | 517 +++--------------- .../Data/SqlClient/Server/SqlDataRecord.cs | 507 +++-------------- 2 files changed, 169 insertions(+), 855 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs index 2e4196a27a..9c588b810b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs @@ -14,12 +14,12 @@ namespace Microsoft.Data.SqlClient.Server /// public class SqlDataRecord : IDataRecord { - private SmiRecordBuffer _recordBuffer; - private SmiExtendedMetaData[] _columnSmiMetaData; - private SmiEventSink_Default _eventSink; - private SqlMetaData[] _columnMetaData; + private readonly SmiRecordBuffer _recordBuffer; + private readonly SmiExtendedMetaData[] _columnSmiMetaData; + private readonly SmiEventSink_Default _eventSink; + private readonly SqlMetaData[] _columnMetaData; private FieldNameLookup _fieldNameLookup; - private bool _usesStringStorageForXml; + private readonly bool _usesStringStorageForXml; private static readonly SmiMetaData s_maxNVarCharForXml = new SmiMetaData( SqlDbType.NVarChar, @@ -32,26 +32,14 @@ public class SqlDataRecord : IDataRecord ); /// - public virtual int FieldCount - { - get - { - EnsureSubclassOverride(); - return _columnMetaData.Length; - } - } + public virtual int FieldCount => _columnMetaData.Length; /// - public virtual string GetName(int ordinal) - { - EnsureSubclassOverride(); - return GetSqlMetaData(ordinal).Name; - } + public virtual string GetName(int ordinal) => GetSqlMetaData(ordinal).Name; /// public virtual string GetDataTypeName(int ordinal) { - EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); if (metaData.SqlDbType == SqlDbType.Udt) { @@ -64,25 +52,14 @@ public virtual string GetDataTypeName(int ordinal) } /// - public virtual Type GetFieldType(int ordinal) - { - EnsureSubclassOverride(); - SqlMetaData md = GetSqlMetaData(ordinal); - return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).ClassType; - } + public virtual Type GetFieldType(int ordinal) => MetaType.GetMetaTypeFromSqlDbType(GetSqlMetaData(ordinal).SqlDbType, false).ClassType; /// - public virtual object GetValue(int ordinal) - { - EnsureSubclassOverride(); - SmiMetaData metaData = GetSmiMetaData(ordinal); - return ValueUtilsSmi.GetValue200(_eventSink, _recordBuffer, ordinal, metaData); - } + public virtual object GetValue(int ordinal) => ValueUtilsSmi.GetValue200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// public virtual int GetValues(object[] values) { - EnsureSubclassOverride(); if (values == null) { throw ADP.ArgumentNull(nameof(values)); @@ -100,8 +77,7 @@ public virtual int GetValues(object[] values) /// public virtual int GetOrdinal(string name) { - EnsureSubclassOverride(); - if (null == _fieldNameLookup) + if (_fieldNameLookup == null) { string[] names = new string[FieldCount]; for (int i = 0; i < names.Length; i++) @@ -116,106 +92,47 @@ public virtual int GetOrdinal(string name) } /// - public virtual object this[int ordinal] - { - get - { - EnsureSubclassOverride(); - return GetValue(ordinal); - } - } + public virtual object this[int ordinal] => GetValue(ordinal); /// - public virtual object this[string name] - { - get - { - EnsureSubclassOverride(); - return GetValue(GetOrdinal(name)); - } - } + public virtual object this[string name] => GetValue(GetOrdinal(name)); /// - public virtual bool GetBoolean(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual bool GetBoolean(int ordinal) => ValueUtilsSmi.GetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual byte GetByte(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual byte GetByte(int ordinal) => ValueUtilsSmi.GetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length, throwOnNull: true); - } + public virtual long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) => ValueUtilsSmi.GetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length, throwOnNull: true); /// - public virtual char GetChar(int ordinal) - { - EnsureSubclassOverride(); - throw ADP.NotSupported(); - } + public virtual char GetChar(int ordinal) => throw ADP.NotSupported(); /// - public virtual long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); - } + public virtual long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) => ValueUtilsSmi.GetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); /// - public virtual Guid GetGuid(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual Guid GetGuid(int ordinal) => ValueUtilsSmi.GetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual short GetInt16(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual short GetInt16(int ordinal) => ValueUtilsSmi.GetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual int GetInt32(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual int GetInt32(int ordinal) => ValueUtilsSmi.GetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual long GetInt64(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual long GetInt64(int ordinal) => ValueUtilsSmi.GetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual float GetFloat(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual float GetFloat(int ordinal) => ValueUtilsSmi.GetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual double GetDouble(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual double GetDouble(int ordinal) => ValueUtilsSmi.GetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// public virtual string GetString(int ordinal) { - EnsureSubclassOverride(); SmiMetaData colMeta = GetSmiMetaData(ordinal); if (_usesStringStorageForXml && colMeta.SqlDbType == SqlDbType.Xml) { @@ -223,42 +140,25 @@ public virtual string GetString(int ordinal) } else { - return ValueUtilsSmi.GetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); + return ValueUtilsSmi.GetString(_eventSink, _recordBuffer, ordinal, colMeta); } } /// - public virtual decimal GetDecimal(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual decimal GetDecimal(int ordinal) => ValueUtilsSmi.GetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual DateTime GetDateTime(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual DateTime GetDateTime(int ordinal) => ValueUtilsSmi.GetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual DateTimeOffset GetDateTimeOffset(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual DateTimeOffset GetDateTimeOffset(int ordinal) => ValueUtilsSmi.GetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual TimeSpan GetTimeSpan(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual TimeSpan GetTimeSpan(int ordinal) => ValueUtilsSmi.GetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// public virtual bool IsDBNull(int ordinal) { - EnsureSubclassOverride(); ThrowIfInvalidOrdinal(ordinal); return ValueUtilsSmi.IsDBNull(_eventSink, _recordBuffer, ordinal); } @@ -267,31 +167,20 @@ public virtual bool IsDBNull(int ordinal) // ISqlRecord implementation public virtual SqlMetaData GetSqlMetaData(int ordinal) { - EnsureSubclassOverride(); + ThrowIfInvalidOrdinal(ordinal); return _columnMetaData[ordinal]; } /// - public virtual Type GetSqlFieldType(int ordinal) - { - EnsureSubclassOverride(); - SqlMetaData md = GetSqlMetaData(ordinal); - return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).SqlType; - } + public virtual Type GetSqlFieldType(int ordinal) => MetaType.GetMetaTypeFromSqlDbType(GetSqlMetaData(ordinal).SqlDbType, false).SqlType; /// - public virtual object GetSqlValue(int ordinal) - { - EnsureSubclassOverride(); - SmiMetaData metaData = GetSmiMetaData(ordinal); - return ValueUtilsSmi.GetSqlValue200(_eventSink, _recordBuffer, ordinal, metaData); - } + public virtual object GetSqlValue(int ordinal) => ValueUtilsSmi.GetSqlValue200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// public virtual int GetSqlValues(object[] values) { - EnsureSubclassOverride(); - if (values == null) + if (null == values) { throw ADP.ArgumentNull(nameof(values)); } @@ -306,122 +195,57 @@ public virtual int GetSqlValues(object[] values) } /// - public virtual SqlBinary GetSqlBinary(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlBinary GetSqlBinary(int ordinal) => ValueUtilsSmi.GetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlBytes GetSqlBytes(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlBytes GetSqlBytes(int ordinal) => ValueUtilsSmi.GetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlXml GetSqlXml(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlXml GetSqlXml(int ordinal) => ValueUtilsSmi.GetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlBoolean GetSqlBoolean(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlBoolean GetSqlBoolean(int ordinal) => ValueUtilsSmi.GetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlByte GetSqlByte(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlByte GetSqlByte(int ordinal) => ValueUtilsSmi.GetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlChars GetSqlChars(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlChars GetSqlChars(int ordinal) => ValueUtilsSmi.GetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlInt16 GetSqlInt16(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlInt16 GetSqlInt16(int ordinal) => ValueUtilsSmi.GetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlInt32 GetSqlInt32(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlInt32 GetSqlInt32(int ordinal) => ValueUtilsSmi.GetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlInt64 GetSqlInt64(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlInt64 GetSqlInt64(int ordinal) => ValueUtilsSmi.GetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlSingle GetSqlSingle(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlSingle GetSqlSingle(int ordinal) => ValueUtilsSmi.GetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlDouble GetSqlDouble(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlDouble GetSqlDouble(int ordinal) => ValueUtilsSmi.GetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlMoney GetSqlMoney(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlMoney GetSqlMoney(int ordinal) => ValueUtilsSmi.GetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlDateTime GetSqlDateTime(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlDateTime GetSqlDateTime(int ordinal) => ValueUtilsSmi.GetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlDecimal GetSqlDecimal(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlDecimal GetSqlDecimal(int ordinal) => ValueUtilsSmi.GetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlString GetSqlString(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlString GetSqlString(int ordinal) => ValueUtilsSmi.GetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlGuid GetSqlGuid(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlGuid GetSqlGuid(int ordinal) => ValueUtilsSmi.GetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// // ISqlUpdateableRecord Implementation public virtual int SetValues(params object[] values) { - EnsureSubclassOverride(); if (values == null) { throw ADP.ArgumentNull(nameof(values)); @@ -461,7 +285,6 @@ public virtual int SetValues(params object[] values) /// public virtual void SetValue(int ordinal, object value) { - EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( metaData.SqlDbType, @@ -478,234 +301,106 @@ public virtual void SetValue(int ordinal, object value) } /// - public virtual void SetBoolean(int ordinal, bool value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetBoolean(int ordinal, bool value) => ValueUtilsSmi.SetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetByte(int ordinal, byte value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetByte(int ordinal, byte value) => ValueUtilsSmi.SetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); - } + public virtual void SetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) => ValueUtilsSmi.SetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); /// - public virtual void SetChar(int ordinal, char value) - { - EnsureSubclassOverride(); - throw ADP.NotSupported(); - } + public virtual void SetChar(int ordinal, char value) => throw ADP.NotSupported(); /// - public virtual void SetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); - } + public virtual void SetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) => ValueUtilsSmi.SetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); /// - public virtual void SetInt16(int ordinal, short value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetInt16(int ordinal, short value) => ValueUtilsSmi.SetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetInt32(int ordinal, int value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetInt32(int ordinal, int value) => ValueUtilsSmi.SetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetInt64(int ordinal, long value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetInt64(int ordinal, long value) => ValueUtilsSmi.SetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetFloat(int ordinal, float value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetFloat(int ordinal, float value) => ValueUtilsSmi.SetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetDouble(int ordinal, double value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetDouble(int ordinal, double value) => ValueUtilsSmi.SetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetString(int ordinal, string value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetString(int ordinal, string value) => ValueUtilsSmi.SetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetDecimal(int ordinal, decimal value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetDecimal(int ordinal, decimal value) => ValueUtilsSmi.SetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetDateTime(int ordinal, DateTime value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetDateTime(int ordinal, DateTime value) => ValueUtilsSmi.SetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetTimeSpan(int ordinal, TimeSpan value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetTimeSpan(int ordinal, TimeSpan value) => ValueUtilsSmi.SetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetDateTimeOffset(int ordinal, DateTimeOffset value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetDateTimeOffset(int ordinal, DateTimeOffset value) => ValueUtilsSmi.SetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// public virtual void SetDBNull(int ordinal) { - EnsureSubclassOverride(); + ThrowIfInvalidOrdinal(ordinal); ValueUtilsSmi.SetDBNull(_eventSink, _recordBuffer, ordinal, true); } /// - public virtual void SetGuid(int ordinal, Guid value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetGuid(int ordinal, Guid value) => ValueUtilsSmi.SetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlBoolean(int ordinal, SqlBoolean value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlBoolean(int ordinal, SqlBoolean value) => ValueUtilsSmi.SetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlByte(int ordinal, SqlByte value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlByte(int ordinal, SqlByte value) => ValueUtilsSmi.SetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlInt16(int ordinal, SqlInt16 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlInt16(int ordinal, SqlInt16 value) => ValueUtilsSmi.SetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlInt32(int ordinal, SqlInt32 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlInt32(int ordinal, SqlInt32 value) => ValueUtilsSmi.SetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlInt64(int ordinal, SqlInt64 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlInt64(int ordinal, SqlInt64 value) => ValueUtilsSmi.SetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlSingle(int ordinal, SqlSingle value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlSingle(int ordinal, SqlSingle value) => ValueUtilsSmi.SetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlDouble(int ordinal, SqlDouble value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlDouble(int ordinal, SqlDouble value) => ValueUtilsSmi.SetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlMoney(int ordinal, SqlMoney value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlMoney(int ordinal, SqlMoney value) => ValueUtilsSmi.SetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlDateTime(int ordinal, SqlDateTime value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlDateTime(int ordinal, SqlDateTime value) => ValueUtilsSmi.SetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlXml(int ordinal, SqlXml value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlXml(int ordinal, SqlXml value) => ValueUtilsSmi.SetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlDecimal(int ordinal, SqlDecimal value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlDecimal(int ordinal, SqlDecimal value) => ValueUtilsSmi.SetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlString(int ordinal, SqlString value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlString(int ordinal, SqlString value) => ValueUtilsSmi.SetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlBinary(int ordinal, SqlBinary value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlBinary(int ordinal, SqlBinary value) => ValueUtilsSmi.SetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlGuid(int ordinal, SqlGuid value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlGuid(int ordinal, SqlGuid value) => ValueUtilsSmi.SetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlChars(int ordinal, SqlChars value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlChars(int ordinal, SqlChars value) => ValueUtilsSmi.SetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlBytes(int ordinal, SqlBytes value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlBytes(int ordinal, SqlBytes value) => ValueUtilsSmi.SetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); // SqlDataRecord public API /// @@ -735,47 +430,18 @@ public SqlDataRecord(params SqlMetaData[] metaData) _eventSink.ProcessMessagesAndThrow(); } - internal SqlDataRecord(SmiRecordBuffer recordBuffer, params SmiExtendedMetaData[] metaData) - { - Debug.Assert(recordBuffer != null, "invalid attempt to instantiate SqlDataRecord with null SmiRecordBuffer"); - Debug.Assert(metaData != null, "invalid attempt to instantiate SqlDataRecord with null SmiExtendedMetaData[]"); - - _columnMetaData = new SqlMetaData[metaData.Length]; - _columnSmiMetaData = new SmiExtendedMetaData[metaData.Length]; - for (int i = 0; i < _columnSmiMetaData.Length; i++) - { - _columnSmiMetaData[i] = metaData[i]; - _columnMetaData[i] = MetaDataUtilsSmi.SmiExtendedMetaDataToSqlMetaData(_columnSmiMetaData[i]); - } - - _eventSink = new SmiEventSink_Default(); - _recordBuffer = recordBuffer; - _eventSink.ProcessMessagesAndThrow(); - } - // // SqlDataRecord private members // - internal SmiRecordBuffer RecordBuffer - { // used by SqlPipe - get - { - return _recordBuffer; - } - } + internal SmiRecordBuffer RecordBuffer => _recordBuffer; - internal SqlMetaData[] InternalGetMetaData() - { - return _columnMetaData; - } + internal SqlMetaData[] InternalGetMetaData() => _columnMetaData; - internal SmiExtendedMetaData[] InternalGetSmiMetaData() - { - return _columnSmiMetaData; - } + internal SmiExtendedMetaData[] InternalGetSmiMetaData() => _columnSmiMetaData; internal SmiExtendedMetaData GetSmiMetaData(int ordinal) { + ThrowIfInvalidOrdinal(ordinal); return _columnSmiMetaData[ordinal]; } @@ -787,20 +453,9 @@ internal void ThrowIfInvalidOrdinal(int ordinal) } } - private void EnsureSubclassOverride() - { - if (_recordBuffer == null) - { - throw SQL.SubclassMustOverride(); - } - } - /// [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - IDataReader System.Data.IDataRecord.GetData(int ordinal) - { - throw ADP.NotSupported(); - } + IDataReader System.Data.IDataRecord.GetData(int ordinal) => throw ADP.NotSupported(); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs index 763469c004..88db0bb49b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs @@ -14,13 +14,13 @@ namespace Microsoft.Data.SqlClient.Server /// public class SqlDataRecord : IDataRecord { - private SmiRecordBuffer _recordBuffer; - private SmiContext _recordContext; - private SmiExtendedMetaData[] _columnSmiMetaData; - private SmiEventSink_Default _eventSink; - private SqlMetaData[] _columnMetaData; + private readonly SmiRecordBuffer _recordBuffer; + private readonly SmiContext _recordContext; + private readonly SmiExtendedMetaData[] _columnSmiMetaData; + private readonly SmiEventSink_Default _eventSink; + private readonly SqlMetaData[] _columnMetaData; + private readonly bool _usesStringStorageForXml; private FieldNameLookup _fieldNameLookup; - private bool _usesStringStorageForXml; static readonly SmiMetaData s_maxNVarCharForXml = new SmiMetaData( SqlDbType.NVarChar, @@ -33,26 +33,14 @@ public class SqlDataRecord : IDataRecord ); /// - public virtual int FieldCount - { - get - { - EnsureSubclassOverride(); - return _columnMetaData.Length; - } - } + public virtual int FieldCount => _columnMetaData.Length; /// - public virtual string GetName(int ordinal) - { - EnsureSubclassOverride(); - return GetSqlMetaData(ordinal).Name; - } + public virtual string GetName(int ordinal) => GetSqlMetaData(ordinal).Name; /// public virtual string GetDataTypeName(int ordinal) { - EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); if (metaData.SqlDbType == SqlDbType.Udt) { @@ -67,14 +55,13 @@ public virtual string GetDataTypeName(int ordinal) /// public virtual Type GetFieldType(int ordinal) { - EnsureSubclassOverride(); - if (GetSqlMetaData(ordinal).SqlDbType == SqlDbType.Udt) + SqlMetaData md = GetSqlMetaData(ordinal); + if (md.SqlDbType == SqlDbType.Udt) { - return GetSqlMetaData(ordinal).Type; + return md.Type; } else { - SqlMetaData md = GetSqlMetaData(ordinal); return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).ClassType; } } @@ -82,9 +69,7 @@ public virtual Type GetFieldType(int ordinal) /// public virtual object GetValue(int ordinal) { - EnsureSubclassOverride(); SmiMetaData metaData = GetSmiMetaData(ordinal); - if (SmiVersion >= SmiContextFactory.KatmaiVersion) { return ValueUtilsSmi.GetValue200(_eventSink, _recordBuffer, ordinal, metaData, _recordContext); @@ -98,7 +83,6 @@ public virtual object GetValue(int ordinal) /// public virtual int GetValues(object[] values) { - EnsureSubclassOverride(); if (values == null) { throw ADP.ArgumentNull(nameof(values)); @@ -116,7 +100,6 @@ public virtual int GetValues(object[] values) /// public virtual int GetOrdinal(string name) { - EnsureSubclassOverride(); if (null == _fieldNameLookup) { string[] names = new string[FieldCount]; @@ -132,106 +115,53 @@ public virtual int GetOrdinal(string name) } /// - public virtual object this[int ordinal] - { - get - { - EnsureSubclassOverride(); - return GetValue(ordinal); - } - } + public virtual object this[int ordinal] => GetValue(ordinal); /// - public virtual object this[string name] - { - get - { - EnsureSubclassOverride(); - return GetValue(GetOrdinal(name)); - } - } + public virtual object this[string name] => GetValue(GetOrdinal(name)); /// - public virtual bool GetBoolean(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual bool GetBoolean(int ordinal) => ValueUtilsSmi.GetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual byte GetByte(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual byte GetByte(int ordinal) => ValueUtilsSmi.GetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// public virtual long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) { - EnsureSubclassOverride(); return ValueUtilsSmi.GetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length, throwOnNull: true); } /// - public virtual char GetChar(int ordinal) - { - EnsureSubclassOverride(); - throw ADP.NotSupported(); - } + public virtual char GetChar(int ordinal) => throw ADP.NotSupported(); /// public virtual long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) { - EnsureSubclassOverride(); return ValueUtilsSmi.GetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); } /// - public virtual Guid GetGuid(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual Guid GetGuid(int ordinal) => ValueUtilsSmi.GetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual short GetInt16(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual short GetInt16(int ordinal) => ValueUtilsSmi.GetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual int GetInt32(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual int GetInt32(int ordinal) => ValueUtilsSmi.GetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual long GetInt64(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual long GetInt64(int ordinal) => ValueUtilsSmi.GetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual float GetFloat(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual float GetFloat(int ordinal) => ValueUtilsSmi.GetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual double GetDouble(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual double GetDouble(int ordinal) => ValueUtilsSmi.GetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// public virtual string GetString(int ordinal) { - EnsureSubclassOverride(); SmiMetaData colMeta = GetSmiMetaData(ordinal); if (_usesStringStorageForXml && colMeta.SqlDbType == SqlDbType.Xml) { @@ -244,37 +174,20 @@ public virtual string GetString(int ordinal) } /// - public virtual decimal GetDecimal(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual decimal GetDecimal(int ordinal) => ValueUtilsSmi.GetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual DateTime GetDateTime(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual DateTime GetDateTime(int ordinal) => ValueUtilsSmi.GetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual DateTimeOffset GetDateTimeOffset(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual DateTimeOffset GetDateTimeOffset(int ordinal) => ValueUtilsSmi.GetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual TimeSpan GetTimeSpan(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual TimeSpan GetTimeSpan(int ordinal) => ValueUtilsSmi.GetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// public virtual bool IsDBNull(int ordinal) { - EnsureSubclassOverride(); ThrowIfInvalidOrdinal(ordinal); return ValueUtilsSmi.IsDBNull(_eventSink, _recordBuffer, ordinal); } @@ -283,14 +196,13 @@ public virtual bool IsDBNull(int ordinal) // ISqlRecord implementation public virtual SqlMetaData GetSqlMetaData(int ordinal) { - EnsureSubclassOverride(); + ThrowIfInvalidOrdinal(ordinal); return _columnMetaData[ordinal]; } /// public virtual Type GetSqlFieldType(int ordinal) { - EnsureSubclassOverride(); SqlMetaData md = GetSqlMetaData(ordinal); return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).SqlType; } @@ -298,7 +210,6 @@ public virtual Type GetSqlFieldType(int ordinal) /// public virtual object GetSqlValue(int ordinal) { - EnsureSubclassOverride(); SmiMetaData metaData = GetSmiMetaData(ordinal); if (SmiVersion >= SmiContextFactory.KatmaiVersion) { @@ -310,7 +221,6 @@ public virtual object GetSqlValue(int ordinal) /// public virtual int GetSqlValues(object[] values) { - EnsureSubclassOverride(); if (values == null) { throw ADP.ArgumentNull(nameof(values)); @@ -326,122 +236,57 @@ public virtual int GetSqlValues(object[] values) } /// - public virtual SqlBinary GetSqlBinary(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlBinary GetSqlBinary(int ordinal) => ValueUtilsSmi.GetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlBytes GetSqlBytes(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); - } + public virtual SqlBytes GetSqlBytes(int ordinal) => ValueUtilsSmi.GetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); /// - public virtual SqlXml GetSqlXml(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); - } + public virtual SqlXml GetSqlXml(int ordinal) => ValueUtilsSmi.GetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); /// - public virtual SqlBoolean GetSqlBoolean(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlBoolean GetSqlBoolean(int ordinal) => ValueUtilsSmi.GetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlByte GetSqlByte(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlByte GetSqlByte(int ordinal) => ValueUtilsSmi.GetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlChars GetSqlChars(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); - } + public virtual SqlChars GetSqlChars(int ordinal) => ValueUtilsSmi.GetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), _recordContext); /// - public virtual SqlInt16 GetSqlInt16(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlInt16 GetSqlInt16(int ordinal) => ValueUtilsSmi.GetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlInt32 GetSqlInt32(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlInt32 GetSqlInt32(int ordinal) => ValueUtilsSmi.GetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlInt64 GetSqlInt64(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlInt64 GetSqlInt64(int ordinal) => ValueUtilsSmi.GetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlSingle GetSqlSingle(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlSingle GetSqlSingle(int ordinal) => ValueUtilsSmi.GetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlDouble GetSqlDouble(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlDouble GetSqlDouble(int ordinal) => ValueUtilsSmi.GetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlMoney GetSqlMoney(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlMoney GetSqlMoney(int ordinal) => ValueUtilsSmi.GetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlDateTime GetSqlDateTime(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlDateTime GetSqlDateTime(int ordinal) => ValueUtilsSmi.GetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlDecimal GetSqlDecimal(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlDecimal GetSqlDecimal(int ordinal) => ValueUtilsSmi.GetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlString GetSqlString(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlString GetSqlString(int ordinal) => ValueUtilsSmi.GetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// - public virtual SqlGuid GetSqlGuid(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } + public virtual SqlGuid GetSqlGuid(int ordinal) => ValueUtilsSmi.GetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); /// // ISqlUpdateableRecord Implementation public virtual int SetValues(params object[] values) { - EnsureSubclassOverride(); if (values == null) { throw ADP.ArgumentNull(nameof(values)); @@ -489,7 +334,6 @@ public virtual int SetValues(params object[] values) /// public virtual void SetValue(int ordinal, object value) { - EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( metaData.SqlDbType, @@ -514,241 +358,113 @@ public virtual void SetValue(int ordinal, object value) } /// - public virtual void SetBoolean(int ordinal, bool value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetBoolean(int ordinal, bool value) => ValueUtilsSmi.SetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetByte(int ordinal, byte value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetByte(int ordinal, byte value) => ValueUtilsSmi.SetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); - } + public virtual void SetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) => ValueUtilsSmi.SetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); /// - public virtual void SetChar(int ordinal, char value) - { - EnsureSubclassOverride(); - throw ADP.NotSupported(); - } + public virtual void SetChar(int ordinal, char value) => throw ADP.NotSupported(); /// - public virtual void SetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); - } + public virtual void SetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) => ValueUtilsSmi.SetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); /// - public virtual void SetInt16(int ordinal, short value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetInt16(int ordinal, short value) => ValueUtilsSmi.SetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetInt32(int ordinal, int value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetInt32(int ordinal, int value) => ValueUtilsSmi.SetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetInt64(int ordinal, long value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetInt64(int ordinal, long value) => ValueUtilsSmi.SetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetFloat(int ordinal, float value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetFloat(int ordinal, float value) => ValueUtilsSmi.SetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetDouble(int ordinal, double value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetDouble(int ordinal, double value) => ValueUtilsSmi.SetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetString(int ordinal, string value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetString(int ordinal, string value) => ValueUtilsSmi.SetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetDecimal(int ordinal, decimal value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetDecimal(int ordinal, decimal value) => ValueUtilsSmi.SetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetDateTime(int ordinal, DateTime value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetDateTime(int ordinal, DateTime value) => ValueUtilsSmi.SetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetTimeSpan(int ordinal, TimeSpan value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, SmiVersion >= SmiContextFactory.KatmaiVersion); - } + public virtual void SetTimeSpan(int ordinal, TimeSpan value) => ValueUtilsSmi.SetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, SmiVersion >= SmiContextFactory.KatmaiVersion); /// - public virtual void SetDateTimeOffset(int ordinal, DateTimeOffset value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, SmiVersion >= SmiContextFactory.KatmaiVersion); - } + public virtual void SetDateTimeOffset(int ordinal, DateTimeOffset value) => ValueUtilsSmi.SetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, SmiVersion >= SmiContextFactory.KatmaiVersion); /// public virtual void SetDBNull(int ordinal) { - EnsureSubclassOverride(); + ThrowIfInvalidOrdinal(ordinal); ValueUtilsSmi.SetDBNull(_eventSink, _recordBuffer, ordinal, true); } /// - public virtual void SetGuid(int ordinal, Guid value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetGuid(int ordinal, Guid value) => ValueUtilsSmi.SetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlBoolean(int ordinal, SqlBoolean value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlBoolean(int ordinal, SqlBoolean value) => ValueUtilsSmi.SetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlByte(int ordinal, SqlByte value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlByte(int ordinal, SqlByte value) => ValueUtilsSmi.SetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlInt16(int ordinal, SqlInt16 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlInt16(int ordinal, SqlInt16 value) => ValueUtilsSmi.SetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlInt32(int ordinal, SqlInt32 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlInt32(int ordinal, SqlInt32 value) => ValueUtilsSmi.SetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlInt64(int ordinal, SqlInt64 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlInt64(int ordinal, SqlInt64 value) => ValueUtilsSmi.SetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlSingle(int ordinal, SqlSingle value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlSingle(int ordinal, SqlSingle value) => ValueUtilsSmi.SetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlDouble(int ordinal, SqlDouble value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlDouble(int ordinal, SqlDouble value) => ValueUtilsSmi.SetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlMoney(int ordinal, SqlMoney value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlMoney(int ordinal, SqlMoney value) => ValueUtilsSmi.SetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlDateTime(int ordinal, SqlDateTime value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlDateTime(int ordinal, SqlDateTime value) => ValueUtilsSmi.SetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlXml(int ordinal, SqlXml value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlXml(int ordinal, SqlXml value) => ValueUtilsSmi.SetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlDecimal(int ordinal, SqlDecimal value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlDecimal(int ordinal, SqlDecimal value) => ValueUtilsSmi.SetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlString(int ordinal, SqlString value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlString(int ordinal, SqlString value) => ValueUtilsSmi.SetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlBinary(int ordinal, SqlBinary value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlBinary(int ordinal, SqlBinary value) => ValueUtilsSmi.SetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlGuid(int ordinal, SqlGuid value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlGuid(int ordinal, SqlGuid value) => ValueUtilsSmi.SetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlChars(int ordinal, SqlChars value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlChars(int ordinal, SqlChars value) => ValueUtilsSmi.SetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); /// - public virtual void SetSqlBytes(int ordinal, SqlBytes value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } + public virtual void SetSqlBytes(int ordinal, SqlBytes value) => ValueUtilsSmi.SetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); // SqlDataRecord public API /// public SqlDataRecord(params SqlMetaData[] metaData) { // Initial consistency check - if (metaData == null) + if (null == metaData) { throw ADP.ArgumentNull(nameof(metaData)); } @@ -787,71 +503,25 @@ public SqlDataRecord(params SqlMetaData[] metaData) _eventSink.ProcessMessagesAndThrow(); } - internal SqlDataRecord(SmiRecordBuffer recordBuffer, params SmiExtendedMetaData[] metaData) - { - Debug.Assert(recordBuffer != null, "invalid attempt to instantiate SqlDataRecord with null SmiRecordBuffer"); - Debug.Assert(metaData != null, "invalid attempt to instantiate SqlDataRecord with null SmiExtendedMetaData[]"); - - _columnMetaData = new SqlMetaData[metaData.Length]; - _columnSmiMetaData = new SmiExtendedMetaData[metaData.Length]; - for (int i = 0; i < _columnSmiMetaData.Length; i++) - { - _columnSmiMetaData[i] = metaData[i]; - _columnMetaData[i] = MetaDataUtilsSmi.SmiExtendedMetaDataToSqlMetaData(_columnSmiMetaData[i]); - } - - _eventSink = new SmiEventSink_Default(); - if (InOutOfProcHelper.InProc) - { - _recordContext = SmiContextFactory.Instance.GetCurrentContext(); - } - else - { - _recordContext = null; - } - _recordBuffer = recordBuffer; - _eventSink.ProcessMessagesAndThrow(); - } - // // SqlDataRecord private members // - internal SmiRecordBuffer RecordBuffer - { // used by SqlPipe - get - { - return _recordBuffer; - } - } + internal SmiRecordBuffer RecordBuffer => _recordBuffer; - internal SmiContext RecordContext - { - get - { - return _recordContext; - } - } + internal SmiContext RecordContext => _recordContext; - private ulong SmiVersion - { - get - { - return InOutOfProcHelper.InProc ? SmiContextFactory.Instance.NegotiatedSmiVersion : SmiContextFactory.LatestVersion; - } - } + private ulong SmiVersion => InOutOfProcHelper.InProc ? SmiContextFactory.Instance.NegotiatedSmiVersion : SmiContextFactory.LatestVersion; internal SqlMetaData[] InternalGetMetaData() { return _columnMetaData; } - internal SmiExtendedMetaData[] InternalGetSmiMetaData() - { - return _columnSmiMetaData; - } + internal SmiExtendedMetaData[] InternalGetSmiMetaData() => _columnSmiMetaData; internal SmiExtendedMetaData GetSmiMetaData(int ordinal) { + ThrowIfInvalidOrdinal(ordinal); return _columnSmiMetaData[ordinal]; } @@ -863,20 +533,9 @@ internal void ThrowIfInvalidOrdinal(int ordinal) } } - private void EnsureSubclassOverride() - { - if (_recordBuffer == null) - { - throw SQL.SubclassMustOverride(); - } - } - /// [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - IDataReader System.Data.IDataRecord.GetData(int ordinal) - { - throw ADP.NotSupported(); - } + IDataReader System.Data.IDataRecord.GetData(int ordinal) => throw ADP.NotSupported(); } } From e0fdd53fceca58b2482643ddc1f58f71ad0a25be Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Fri, 25 Jun 2021 03:04:06 +0000 Subject: [PATCH 87/87] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 2 +- .../netfx/src/Resources/Strings.es.resx | 2 +- .../netfx/src/Resources/Strings.fr.resx | 2 +- .../netfx/src/Resources/Strings.it.resx | 2 +- .../netfx/src/Resources/Strings.ja.resx | 2 +- .../netfx/src/Resources/Strings.ko.resx | 2 +- .../netfx/src/Resources/Strings.pt-BR.resx | 2 +- .../netfx/src/Resources/Strings.ru.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hans.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hant.resx | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 3e7fc72ca7..cb8cc0c931 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -3048,7 +3048,7 @@ Die Unterklasse hat keine erforderliche Methode überschrieben. - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index 69a8f3a430..4d986017d3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -3048,7 +3048,7 @@ La subclase no invalidó un método requerido. - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index 46f0ab2621..cf74128a35 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -3048,7 +3048,7 @@ La sous-classe n'a pas substituée une méthode requise. - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 458f318968..2262277b25 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -3048,7 +3048,7 @@ La sottoclasse non ha eseguito l'override di un metodo di richiesta. - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index 88a30ebd55..e3a51bf472 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -3048,7 +3048,7 @@ サブクラスが、必要なメソッドをオーバーライドしませんでした。 - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 15eb33db16..b62b69c42e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -3048,7 +3048,7 @@ 서브클래스에서 필요한 메서드를 재정의하지 않았습니다. - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index 8a46736b4b..cf7bda0055 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -3048,7 +3048,7 @@ A subclasse não substituiu um método necessário. - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index c811c6e03f..f1d26299b8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -3048,7 +3048,7 @@ Подклассы не переопределяют необходимый метод. - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index a141a0054b..5d73f0a005 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -3048,7 +3048,7 @@ 子类未覆盖所需方法。 - + Cannot create normalizer for '{0}'. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 9134b804a0..10d56b105a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -3048,7 +3048,7 @@ 子類別並未覆寫所需的方法。 - + Cannot create normalizer for '{0}'.