From 20e1535b28b13fa1015556a84ccaeb47e7f498c6 Mon Sep 17 00:00:00 2001 From: Lawrence Cheung <31262254+lcheunglci@users.noreply.github.com> Date: Mon, 27 Mar 2023 15:41:03 -0400 Subject: [PATCH 01/76] [5.1.1] Fix | Addressing failure on providing correct error message when symmetric key decryption fails using Always Encrypted. (#1968) --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 7 +++++++ .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 7 +++++++ 2 files changed, 14 insertions(+) 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 8217604c72..ba650cc932 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 @@ -6070,6 +6070,13 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T } catch (Exception e) { + if (stateObj is not null) + { + // call to decrypt column keys has failed. The data wont be decrypted. + // Not setting the value to false, forces the driver to look for column value. + // Packet received from Key Vault will throws invalid token header. + stateObj.HasPendingData = false; + } throw SQL.ColumnDecryptionFailed(columnName, null, e); } } 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 70673ef343..466414c425 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 @@ -6884,6 +6884,13 @@ internal bool TryReadSqlValue(SqlBuffer value, } catch (Exception e) { + if (stateObj is not null) + { + // call to decrypt column keys has failed. The data wont be decrypted. + // Not setting the value to false, forces the driver to look for column value. + // Packet received from Key Vault will throws invalid token header. + stateObj._pendingData = false; + } throw SQL.ColumnDecryptionFailed(columnName, null, e); } } From daa1a746cce976d052840c9e26be1f58bb39f01d Mon Sep 17 00:00:00 2001 From: Lawrence Cheung <31262254+lcheunglci@users.noreply.github.com> Date: Mon, 27 Mar 2023 15:41:44 -0400 Subject: [PATCH 02/76] [5.1.1] Fix | TransactionScope connection issue when Enlist is enable, Pooling is disabled and network connection type is Redirect (#1967) --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 2 +- .../tests/ManualTests/SQL/TransactionTest/TransactionTest.cs | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) 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 e977641175..49c0883e3d 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 @@ -1235,7 +1235,7 @@ private void CompleteLogin(bool enlistOK) // for non-pooled connections, enlist in a distributed transaction // if present - and user specified to enlist - if (enlistOK && ConnectionOptions.Enlist) + if (enlistOK && ConnectionOptions.Enlist && RoutingInfo == null) { _parser._physicalStateObj.SniContext = SniContext.Snix_AutoEnlist; Transaction tx = ADP.GetCurrentTransaction(); 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 ee8d211e7b..748483b49d 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 @@ -1500,7 +1500,7 @@ private void CompleteLogin(bool enlistOK) // for non-pooled connections, enlist in a distributed transaction // if present - and user specified to enlist - if (enlistOK && ConnectionOptions.Enlist) + if (enlistOK && ConnectionOptions.Enlist && _routingInfo == null) { _parser._physicalStateObj.SniContext = SniContext.Snix_AutoEnlist; SysTx.Transaction tx = ADP.GetCurrentTransaction(); 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 9d1c5c6b5f..da1d0cdeab 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs @@ -46,8 +46,7 @@ public static class TransactionTest 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))] [MemberData(nameof(PoolDisabledConnectionStrings))] public static void ReadNextQueryAfterTxAbortedPoolDisabled(string connString) => ReadNextQueryAfterTxAbortedTest(connString); From f70b9c602e0e57f829fcefce4a6a59f402e914d2 Mon Sep 17 00:00:00 2001 From: Lawrence Cheung <31262254+lcheunglci@users.noreply.github.com> Date: Mon, 27 Mar 2023 15:42:32 -0400 Subject: [PATCH 03/76] [5.1.1] Fix | Throttling of token requests by calling AcquireTokenSilent (#1966) --- .../ActiveDirectoryAuthenticationProvider.cs | 193 ++++++++++++------ 1 file changed, 126 insertions(+), 67 deletions(-) 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 573c36ee55..6e57bb6c07 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,10 @@ using System; using System.Collections.Concurrent; -using System.Security; +using System.Linq; +using System.Runtime.Caching; +using System.Security.Cryptography; +using System.Text; using System.Threading; using System.Threading.Tasks; using Azure.Core; @@ -24,6 +27,8 @@ public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationPro /// private static ConcurrentDictionary s_pcaMap = new ConcurrentDictionary(); + private static readonly MemoryCache s_accountPwCache = new(nameof(ActiveDirectoryAuthenticationProvider)); + private static readonly int s_accountPwCacheTtlInHours = 2; private static readonly string s_nativeClientRedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"; private static readonly string s_defaultScopeSuffix = "/.default"; private readonly string _type = typeof(ActiveDirectoryAuthenticationProvider).Name; @@ -172,7 +177,7 @@ public override async Task AcquireTokenAsync(SqlAuthenti return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); } - AuthenticationResult result; + AuthenticationResult result = null; if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal) { AccessToken accessToken = await new ClientSecretCredential(audience, parameters.UserId, parameters.Password, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token).ConfigureAwait(false); @@ -208,82 +213,82 @@ public override async Task AcquireTokenAsync(SqlAuthenti if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryIntegrated) { - if (!string.IsNullOrEmpty(parameters.UserId)) - { - result = await app.AcquireTokenByIntegratedWindowsAuth(scopes) - .WithCorrelationId(parameters.ConnectionId) - .WithUsername(parameters.UserId) - .ExecuteAsync(cancellationToken: cts.Token) - .ConfigureAwait(false); - } - else - { - result = await app.AcquireTokenByIntegratedWindowsAuth(scopes) - .WithCorrelationId(parameters.ConnectionId) - .ExecuteAsync(cancellationToken: cts.Token) - .ConfigureAwait(false); - } - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result?.ExpiresOn); - } - else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryPassword) - { - result = await app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password) - .WithCorrelationId(parameters.ConnectionId) - .ExecuteAsync(cancellationToken: cts.Token) - .ConfigureAwait(false); - - 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) - { - // Fetch available accounts from 'app' instance - System.Collections.Generic.IEnumerator accounts = (await app.GetAccountsAsync().ConfigureAwait(false)).GetEnumerator(); + result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false); - IAccount account = default; - if (accounts.MoveNext()) + if (null == result) { 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()); + result = await app.AcquireTokenByIntegratedWindowsAuth(scopes) + .WithCorrelationId(parameters.ConnectionId) + .WithUsername(parameters.UserId) + .ExecuteAsync(cancellationToken: cts.Token) + .ConfigureAwait(false); } else { - account = accounts.Current; + result = await app.AcquireTokenByIntegratedWindowsAuth(scopes) + .WithCorrelationId(parameters.ConnectionId) + .ExecuteAsync(cancellationToken: cts.Token) + .ConfigureAwait(false); } + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result?.ExpiresOn); + } + } + else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryPassword) + { + string pwCacheKey = GetAccountPwCacheKey(parameters); + object previousPw = s_accountPwCache.Get(pwCacheKey); + byte[] currPwHash = GetHash(parameters.Password); + + if (null != previousPw && + previousPw is byte[] previousPwBytes && + // Only get the cached token if the current password hash matches the previously used password hash + currPwHash.SequenceEqual(previousPwBytes)) + { + result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false); } - if (null != account) + if (null == result) { - try - { - // 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(cancellationToken: cts.Token).ConfigureAwait(false); - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn); - } - catch (MsalUiRequiredException) + result = await app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password) + .WithCorrelationId(parameters.ConnectionId) + .ExecuteAsync(cancellationToken: cts.Token) + .ConfigureAwait(false); + + // We cache the password hash to ensure future connection requests include a validated password + // when we check for a cached MSAL account. Otherwise, a connection request with the same username + // against the same tenant could succeed with an invalid password when we re-use the cached token. + if (!s_accountPwCache.Add(pwCacheKey, GetHash(parameters.Password), DateTime.UtcNow.AddHours(s_accountPwCacheTtlInHours))) { - // 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, cts).ConfigureAwait(false); - SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn); + s_accountPwCache.Remove(pwCacheKey); + s_accountPwCache.Add(pwCacheKey, GetHash(parameters.Password), DateTime.UtcNow.AddHours(s_accountPwCacheTtlInHours)); } + + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result?.ExpiresOn); } - else + } + else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive || + parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow) + { + try + { + result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false); + 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, cts, _customWebUI, _deviceCodeFlowCallback).ConfigureAwait(false); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn); + } + + if (null == result) { // If no existing 'account' is found, we request user to sign in interactively. - result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts).ConfigureAwait(false); + result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts, _customWebUI, _deviceCodeFlowCallback).ConfigureAwait(false); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn); } } @@ -296,8 +301,49 @@ public override async Task AcquireTokenAsync(SqlAuthenti return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn); } - private async Task AcquireTokenInteractiveDeviceFlowAsync(IPublicClientApplication app, string[] scopes, Guid connectionId, string userId, - SqlAuthenticationMethod authenticationMethod, CancellationTokenSource cts) + private static async Task TryAcquireTokenSilent(IPublicClientApplication app, SqlAuthenticationParameters parameters, + string[] scopes, CancellationTokenSource cts) + { + AuthenticationResult result = null; + + // Fetch available accounts from 'app' instance + System.Collections.Generic.IEnumerator accounts = (await app.GetAccountsAsync().ConfigureAwait(false)).GetEnumerator(); + + IAccount account = default; + if (accounts.MoveNext()) + { + 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) + { + // 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(cancellationToken: cts.Token).ConfigureAwait(false); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn); + } + + return result; + } + + private static async Task AcquireTokenInteractiveDeviceFlowAsync(IPublicClientApplication app, string[] scopes, Guid connectionId, string userId, + SqlAuthenticationMethod authenticationMethod, CancellationTokenSource cts, ICustomWebUi customWebUI, Func deviceCodeFlowCallback) { try { @@ -316,11 +362,11 @@ private async Task AcquireTokenInteractiveDeviceFlowAsync( */ ctsInteractive.CancelAfter(180000); #endif - if (_customWebUI != null) + if (customWebUI != null) { return await app.AcquireTokenInteractive(scopes) .WithCorrelationId(connectionId) - .WithCustomWebUi(_customWebUI) + .WithCustomWebUi(customWebUI) .WithLoginHint(userId) .ExecuteAsync(ctsInteractive.Token) .ConfigureAwait(false); @@ -354,7 +400,7 @@ private async Task AcquireTokenInteractiveDeviceFlowAsync( else { AuthenticationResult result = await app.AcquireTokenWithDeviceCode(scopes, - deviceCodeResult => _deviceCodeFlowCallback(deviceCodeResult)) + deviceCodeResult => deviceCodeFlowCallback(deviceCodeResult)) .WithCorrelationId(connectionId) .ExecuteAsync(cancellationToken: cts.Token) .ConfigureAwait(false); @@ -407,6 +453,19 @@ private IPublicClientApplication GetPublicClientAppInstance(PublicClientAppKey p return clientApplicationInstance; } + private static string GetAccountPwCacheKey(SqlAuthenticationParameters parameters) + { + return parameters.Authority + "+" + parameters.UserId; + } + + private static byte[] GetHash(string input) + { + byte[] unhashedBytes = Encoding.Unicode.GetBytes(input); + SHA256 sha256 = SHA256.Create(); + byte[] hashedBytes = sha256.ComputeHash(unhashedBytes); + return hashedBytes; + } + private IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publicClientAppKey) { IPublicClientApplication publicClientApplication; From 41af66fadc472daa488bd12b8a3d9c43bc334724 Mon Sep 17 00:00:00 2001 From: Lawrence Cheung <31262254+lcheunglci@users.noreply.github.com> Date: Mon, 27 Mar 2023 16:48:03 -0400 Subject: [PATCH 04/76] [5.1.1] Fix | TDS RPC error on large queries in SqlCommand.ExecuteReaderAsync (#1965) --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 6 +- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 123 ++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) 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 466414c425..355bcf8a47 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 @@ -10024,10 +10024,10 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo // Options WriteShort((short)rpcext.options, stateObj); - } - byte[] enclavePackage = cmd.enclavePackage != null ? cmd.enclavePackage.EnclavePackageBytes : null; - WriteEnclaveInfo(stateObj, enclavePackage); + byte[] enclavePackage = cmd.enclavePackage != null ? cmd.enclavePackage.EnclavePackageBytes : null; + WriteEnclaveInfo(stateObj, enclavePackage); + } // Stream out parameters int parametersLength = rpcext.userParamCount + rpcext.systemParamCount; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 5f60faddb5..945badebb1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; @@ -689,6 +690,59 @@ public void TestExecuteReader(string connection) }); } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public async void TestExecuteReaderAsyncWithLargeQuery(string connectionString) + { + string randomName = DataTestUtility.GetUniqueName(Guid.NewGuid().ToString().Replace("-", ""), false); + if (randomName.Length > 50) + { + randomName = randomName.Substring(0, 50); + } + string tableName = $"VeryLong_{randomName}_TestTableName"; + int columnsCount = 50; + + // Arrange - drops the table with long name and re-creates it with 52 columns (ID, name, ColumnName0..49) + try + { + CreateTable(connectionString, tableName, columnsCount); + string name = "nobody"; + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + await connection.OpenAsync(); + // This creates a "select top 100" query that has over 40k characters + using (SqlCommand sqlCommand = new SqlCommand(GenerateSelectQuery(tableName, columnsCount, 10, "WHERE Name = @FirstName AND ID = @CustomerId"), + connection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.VarChar, name.Length); + + sqlCommand.Parameters[0].Value = 0; + sqlCommand.Parameters[1].Value = name; + + // Act and Assert + // Test that execute reader async does not throw an exception. + // The table is empty so there should be no results; however, the bug previously found is that it causes a TDS RPC exception on enclave. + using (SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync()) + { + Assert.False(sqlDataReader.HasRows, "The table should be empty"); + } + } + } + } + catch + { + throw; + } + finally + { + DropTableIfExists(connectionString, tableName); + } + } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet1))] public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehavior commandBehavior) @@ -2806,6 +2860,75 @@ private void CleanUpTable(string connString, string tableName) } } + private static void CreateTable(string connString, string tableName, int columnsCount) + => DataTestUtility.RunNonQuery(connString, GenerateCreateQuery(tableName, columnsCount)); + /// + /// Drops the table if the specified table exists + /// + /// The connection string to the database + /// The name of the table to be dropped + private static void DropTableIfExists(string connString, string tableName) + { + using var sqlConnection = new SqlConnection(connString); + sqlConnection.Open(); + DataTestUtility.DropTable(sqlConnection, tableName); + } + + /// + /// Generates the query for creating a table with the number of bit columns specified. + /// + /// The name of the table + /// The number of columns for the table + /// + private static string GenerateCreateQuery(string tableName, int columnsCount) + { + StringBuilder builder = new StringBuilder(); + builder.Append(string.Format("CREATE TABLE [dbo].[{0}]", tableName)); + builder.Append('('); + builder.AppendLine("[ID][bigint] NOT NULL,"); + builder.AppendLine("[Name] [varchar] (200) NOT NULL"); + for (int i = 0; i < columnsCount; i++) + { + builder.Append(','); + builder.Append($"[ColumnName{i}][bit] NULL"); + } + builder.Append(");"); + + return builder.ToString(); + } + + /// + /// Generates the large query with the select top 100 of all the columns repeated multiple times. + /// + /// The name of the table + /// The number of columns to be explicitly included + /// The number of times the select query is repeated + /// A where clause for additional filters + /// + private static string GenerateSelectQuery(string tableName, int columnsCount, int repeat = 10, string where = "") + { + StringBuilder builder = new StringBuilder(); + builder.AppendLine($"SELECT TOP 100"); + builder.AppendLine($"[{tableName}].[ID],"); + builder.AppendLine($"[{tableName}].[Name]"); + for (int i = 0; i < columnsCount; i++) + { + builder.Append(","); + builder.AppendLine($"[{tableName}].[ColumnName{i}]"); + } + + string extra = string.IsNullOrEmpty(where) ? $"(NOLOCK) [{tableName}]" : where; + builder.AppendLine($"FROM [{tableName}] {extra};"); + + StringBuilder builder2 = new StringBuilder(); + for (int i = 0; i < repeat; i++) + { + builder2.AppendLine(builder.ToString()); + } + + return builder2.ToString(); + } + /// /// An helper method to test the cancellation of the command using cancellationToken to async SqlCommand APIs. /// From 8601b680aa026bc29cce0fea4e84d439e06b92d2 Mon Sep 17 00:00:00 2001 From: Lawrence Cheung <31262254+lcheunglci@users.noreply.github.com> Date: Mon, 27 Mar 2023 16:48:59 -0400 Subject: [PATCH 05/76] [5.1.1] Fix | NullReferenceException in GetBytesAsync (#1964) --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 2 +- .../DataReaderTest/DataReaderStreamsTest.cs | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) 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 8d43d72685..965803f993 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 @@ -5285,7 +5285,7 @@ protected override void DisposeCore() { TDisposable copy = _disposable; _disposable = default; - copy.Dispose(); + copy?.Dispose(); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs index f008d6b2fa..3f16a3a138 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading.Tasks; using System.Xml; +using System.Xml.Linq; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -471,6 +472,37 @@ public static void InvalidCastExceptionStream(CommandBehavior behavior, Accessor } } +#if NETCOREAPP + [ConditionalFact(typeof(DataTestUtility),nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + public static async void ReadAsyncContentsCompletes() + { + string expectedXml = "This is a test string"; + string query = $"SELECT CAST('{expectedXml}' AS NVARCHAR(MAX))"; + + string returnedXml = null; + using (SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlCommand command = new SqlCommand(query, connection)) + { + connection.Open(); + + await using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess).ConfigureAwait(false)) + { + while (await reader.ReadAsync().ConfigureAwait(false)) + { + using (TextReader textReader = reader.GetTextReader(0)) + using (XmlReader xmlReader = XmlReader.Create(textReader, new XmlReaderSettings() { Async = true })) + { + XDocument xdoc = await XDocument.LoadAsync(xmlReader, LoadOptions.None, default).ConfigureAwait(false); + returnedXml = xdoc.ToString(); + } + } + } + } + + Assert.Equal(expectedXml, returnedXml, StringComparer.Ordinal); + } +#endif + private static async Task ExecuteReader(SqlCommand command, CommandBehavior behavior, bool isExecuteAsync) => isExecuteAsync ? await command.ExecuteReaderAsync(behavior) : command.ExecuteReader(behavior); From 39cedc87167220f2f50f394c76ba8f5ae8af7441 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:36:03 -0700 Subject: [PATCH 06/76] Test | Disable SqlConnectionReliabilityTest.ConcurrentExecution test (#1998) (#2074) --- .../ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs | 1 + 1 file changed, 1 insertion(+) 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 624912f260..2c42f456c9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs @@ -98,6 +98,7 @@ public void CreateDatabaseWhileTryingToConnect(string cnnString, SqlRetryLogicBa Assert.True(currentRetries > 0); } + [ActiveIssue(25147)] [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider provider) From 80d1f47be7a4f0a86d87b1d5ca1e962aa780077d Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Mon, 16 Oct 2023 01:32:38 +0000 Subject: [PATCH 07/76] Merged PR 4035: [5.1.2] | Fix Transient fault handling issue with OpenAsync (#1983) Ports [#1983](https://github.com/dotnet/SqlClient/pull/1983) --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +- .../SqlConnectionBasicTests.cs | 85 ++++++++++++++++++- .../TDS.Servers/TransientFaultTDSServer.cs | 1 + 4 files changed, 86 insertions(+), 8 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 660b5934e0..1040061b59 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 @@ -1820,7 +1820,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec } } - _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); if (connectionOptions != null && (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || @@ -1849,7 +1849,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec // does not require GC.KeepAlive(this) because of ReRegisterForFinalize below. // Set future transient fault handling based on connection options - _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + _applyTransientFaultHandling = connectionOptions != null && connectionOptions.ConnectRetryCount > 0; var tdsInnerConnection = (SqlInternalConnectionTds)InnerConnection; 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 04acfcb612..1ab55969a0 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 @@ -2059,7 +2059,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec bool result = false; - _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); if (connectionOptions != null && (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || @@ -2102,7 +2102,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec } // Set future transient fault handling based on connection options - _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + _applyTransientFaultHandling = connectionOptions != null && connectionOptions.ConnectRetryCount > 0; return result; } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index 039bf2f766..e61bdfe4ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -7,6 +7,7 @@ using System.Data.Common; using System.Reflection; using System.Security; +using System.Threading.Tasks; using Microsoft.SqlServer.TDS.Servers; using Xunit; @@ -34,6 +35,26 @@ public void IntegratedAuthConnectionTest() connection.Open(); } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] + [InlineData(40613)] + [InlineData(42108)] + [InlineData(42109)] + [PlatformSpecific(TestPlatforms.Windows)] + public async Task TransientFaultTestAsync(uint errorCode) + { + using TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, errorCode); + SqlConnectionStringBuilder builder = new() + { + DataSource = "localhost," + server.Port, + IntegratedSecurity = true, + Encrypt = SqlConnectionEncryptOption.Optional + }; + + using SqlConnection connection = new(builder.ConnectionString); + await connection.OpenAsync(); + Assert.Equal(ConnectionState.Open, connection.State); + } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] [InlineData(40613)] [InlineData(42108)] @@ -57,14 +78,70 @@ public void TransientFaultTest(uint errorCode) } catch (Exception e) { - if (null != connection) - { - Assert.Equal(ConnectionState.Closed, connection.State); - } Assert.False(true, e.Message); } } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] + [InlineData(40613)] + [InlineData(42108)] + [InlineData(42109)] + [PlatformSpecific(TestPlatforms.Windows)] + public async Task TransientFaultDisabledTestAsync(uint errorCode) + { + using TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, errorCode); + SqlConnectionStringBuilder builder = new() + { + DataSource = "localhost," + server.Port, + IntegratedSecurity = true, + ConnectRetryCount = 0, + Encrypt = SqlConnectionEncryptOption.Optional + }; + + using SqlConnection connection = new(builder.ConnectionString); + try + { + await connection.OpenAsync(); + Assert.False(true, "Connection should not have opened."); + } + catch (SqlException e) + { + // FATAL Error, should result in closed connection. + Assert.Equal(20, e.Class); + Assert.Equal(ConnectionState.Closed, connection.State); + } + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] + [InlineData(40613)] + [InlineData(42108)] + [InlineData(42109)] + [PlatformSpecific(TestPlatforms.Windows)] + public void TransientFaultDisabledTest(uint errorCode) + { + using TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, errorCode); + SqlConnectionStringBuilder builder = new() + { + DataSource = "localhost," + server.Port, + IntegratedSecurity = true, + ConnectRetryCount = 0, + Encrypt = SqlConnectionEncryptOption.Optional + }; + + using SqlConnection connection = new(builder.ConnectionString); + try + { + connection.Open(); + Assert.False(true, "Connection should not have opened."); + } + catch (SqlException e) + { + // FATAL Error, should result in closed connection. + Assert.Equal(20, e.Class); + Assert.Equal(ConnectionState.Closed, connection.State); + } + } + [Fact] public void SqlConnectionDbProviderFactoryTest() { 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 index 419f7e5d24..eda4de8e2a 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs @@ -146,6 +146,7 @@ private void Dispose(bool isDisposing) if (isDisposing) { _endpoint?.Stop(); + RequestCounter = 0; } } } From 0b51b63ef24123f27cfda66005b47630d49dd7ac Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Mon, 16 Oct 2023 20:13:04 +0000 Subject: [PATCH 08/76] Merged PR 4030: [5.1.2] Fix | Adding type convertor support for SqlConnectionEncryptOption (#2057) Ports [#2057](https://github.com/dotnet/SqlClient/pull/2057) --- .../src/Microsoft.Data.SqlClient.csproj | 3 + .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 + .../SqlClient/SqlConnectionEncryptOption.cs | 2 + .../SqlConnectionEncryptOptionConverter.cs | 56 ++++++ .../Microsoft.Data.SqlClient.Tests.csproj | 1 + .../SqlConnectionStringBuilderTest.cs | 160 ++++++++++++++++++ tools/props/Versions.props | 1 + 7 files changed, 226 insertions(+) create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs 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 bc755d3486..5e08a713a5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -328,6 +328,9 @@ Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs + + Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs + Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.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 bd8d67b6e2..a10a693406 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -422,6 +422,9 @@ Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs + + Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs + Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs index fde49b3980..cb5b467298 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs @@ -3,11 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.ComponentModel; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { /// + [TypeConverter(typeof(SqlConnectionEncryptOptionConverter))] public sealed class SqlConnectionEncryptOption { private const string TRUE = "True"; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs new file mode 100644 index 0000000000..0e22e6ce2e --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOptionConverter.cs @@ -0,0 +1,56 @@ +// 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.ComponentModel; +using System.Globalization; +using Microsoft.Data.Common; + +namespace Microsoft.Data.SqlClient +{ + internal class SqlConnectionEncryptOptionConverter : TypeConverter + { + // Overrides the CanConvertFrom method of TypeConverter. + // The ITypeDescriptorContext interface provides the context for the + // conversion. Typically, this interface is used at design time to + // provide information about the design-time container. + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + // Overrides the CanConvertTo method of TypeConverter. + public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertTo(context, sourceType); + } + + // Overrides the ConvertFrom method of TypeConverter. + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return SqlConnectionEncryptOption.Parse(value.ToString()); + } + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionEncryptOption), null); + } + + // Overrides the ConvertTo method of TypeConverter. + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (destinationType == typeof(string)) + { + return base.ConvertTo(context, culture, value, destinationType); + } + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionEncryptOption), null); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index 9d1e8d5087..a90960bdc5 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -66,6 +66,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs index 0825760e45..7e65cec996 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs @@ -4,6 +4,13 @@ using System; using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Text; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -446,6 +453,159 @@ public void EncryptTryParseInvalidValuesReturnsFalse(string value) Assert.Null(result); } + #region SqlConnectionEncryptOptionCoverterTests + [Fact] + public void ConnectionStringFromJsonTests() + { + UserDbConnectionStringSettings settings = LoadSettingsFromJsonStream("false"); + Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt); + + settings = LoadSettingsFromJsonStream("true"); + Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt); + + settings = LoadSettingsFromJsonStream("strict"); + Assert.Equal(SqlConnectionEncryptOption.Strict, settings.UserDb.UserComponents.Encrypt); + + settings = LoadSettingsFromJsonStream("mandatory"); + Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt); + + settings = LoadSettingsFromJsonStream("optional"); + Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt); + + settings = LoadSettingsFromJsonStream("yes"); + Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt); + + settings = LoadSettingsFromJsonStream("no"); + Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt); + } + + [Theory] + [InlineData("absolutely")] + [InlineData("affirmative")] + [InlineData("never")] + [InlineData("always")] + [InlineData("none")] + [InlineData(" for sure ")] + public void ConnectionStringFromJsonThrowsException(string value) + { + ExecuteConnectionStringFromJsonThrowsException(value); + } + + [Fact] + public void SqlConnectionEncryptOptionConverterCanConvertFromTest() + { + // Get a converter + SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false"); + TypeConverter converter = TypeDescriptor.GetConverter(option.GetType()); + // Use the converter to determine if can convert from string data type + Assert.True(converter.CanConvertFrom(null, typeof(string)), "Expecting to convert from a string type."); + // Use the same converter to determine if can convert from int or bool data types + Assert.False(converter.CanConvertFrom(null, typeof(int)), "Not expecting to convert from integer type."); + Assert.False(converter.CanConvertFrom(null, typeof(bool)), "Not expecting to convert from boolean type."); + } + + [Fact] + public void SqlConnectionEncryptOptionConverterCanConvertToTest() + { + // Get a converter + SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false"); + TypeConverter converter = TypeDescriptor.GetConverter(option.GetType()); + // Use the converter to check if can convert from stirng + Assert.True(converter.CanConvertTo(null, typeof(string)), "Expecting to convert to a string type."); + // Use the same convert to check if can convert to int or bool + Assert.False(converter.CanConvertTo(null, typeof(int)), "Not expecting to convert from integer type."); + Assert.False(converter.CanConvertTo(null, typeof(bool)), "Not expecting to convert from boolean type."); + } + + [Fact] + public void SqlConnectionEncryptOptionConverterConvertFromTest() + { + // Create a converter + SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false"); + TypeConverter converter = TypeDescriptor.GetConverter(option.GetType()); + // Use the converter to convert all possible valid values + Assert.Equal(SqlConnectionEncryptOption.Parse("false"), converter.ConvertFrom("false")); + Assert.Equal(SqlConnectionEncryptOption.Parse("true"), converter.ConvertFrom("true")); + Assert.Equal(SqlConnectionEncryptOption.Parse("strict"), converter.ConvertFrom("strict")); + Assert.Equal(SqlConnectionEncryptOption.Parse("mandatory"), converter.ConvertFrom("mandatory")); + Assert.Equal(SqlConnectionEncryptOption.Parse("optional"), converter.ConvertFrom("optional")); + Assert.Equal(SqlConnectionEncryptOption.Parse("yes"), converter.ConvertFrom("yes")); + Assert.Equal(SqlConnectionEncryptOption.Parse("no"), converter.ConvertFrom("no")); + // Use the converter to covert invalid value + Assert.Throws(() => converter.ConvertFrom("affirmative")); + // Use the same converter to convert from bad data types + Assert.Throws(() => converter.ConvertFrom(1)); + Assert.Throws(() => converter.ConvertFrom(true)); + } + + [Fact] + public void SqlConnectionEncryptOptionConverterConvertToTest() + { + // Get a converter + SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false"); + TypeConverter converter = TypeDescriptor.GetConverter(option.GetType()); + // Use the converter to convert all possible valid values to string + Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(string))); + Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("true"), typeof(string))); + Assert.Equal("Strict", converter.ConvertTo(SqlConnectionEncryptOption.Parse("strict"), typeof(string))); + Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("mandatory"), typeof(string))); + Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("optional"), typeof(string))); + Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("yes"), typeof(string))); + Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("no"), typeof(string))); + // Use the same converter to try convert to bad data types + Assert.Throws(() => converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(int))); + Assert.Throws(() => converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(bool))); + } + + internal class UserDbConnectionStringSettings + { + [Required] + public UserSqlConnectionString UserDb { get; set; } + } + + internal class UserSqlConnectionString + { + public SqlConnectionStringBuilder UserComponents { get; set; } = new(); + + public override string ToString() + { + return UserComponents!.ConnectionString; + } + } + + internal static void ExecuteConnectionStringFromJsonThrowsException(string encryptOption) + { + var exception = Assert.Throws(() => LoadSettingsFromJsonStream(encryptOption)); + Assert.Contains("Failed to convert configuration", exception.Message, StringComparison.Ordinal); + } + + private static TSettings LoadSettingsFromJsonStream(string encryptOption) where TSettings : class + { + TSettings settingsOut = null; + + Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((ctx, configBuilder) => + { + // Note: Inside string interpolation, a { should be {{ and a } should be }} + // First, declare a stringified JSON + var json = $"{{ \"UserDb\": {{ \"UserComponents\": {{ \"NetworkLibrary\": \"DBMSSOCN\", \"UserID\": \"user\", \"Password\": \"password\", \"DataSource\": \"localhost\", \"InitialCatalog\": \"catalog\", \"Encrypt\": \"{encryptOption}\" }}}}}}"; + // Load the stringified JSON as a stream into the configuration builder + configBuilder.AddJsonStream(new MemoryStream(Encoding.ASCII.GetBytes(json))); + configBuilder.AddEnvironmentVariables(); + }) + .ConfigureServices((ctx, services) => + { + var configuration = ctx.Configuration; + services.AddOptions(); + services.Configure(ctx.Configuration); + settingsOut = configuration.Get(); + }) + .Build(); + + return settingsOut; + } + #endregion + internal void ExecuteConnectionStringTests(string connectionString) { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString); diff --git a/tools/props/Versions.props b/tools/props/Versions.props index b9a5cb84d7..ada3af2c63 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -73,6 +73,7 @@ 10.50.1600.1 0.13.2 6.0.0 + 6.0.0 $(NugetPackageVersion) From 1dba81cd21081c584da19dcd53d917292dbcb647 Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Mon, 16 Oct 2023 21:15:28 +0000 Subject: [PATCH 09/76] Merged PR 4034: [5.1.2] | Update SNI version (#2123) Ports [#2123](https://github.com/dotnet/SqlClient/pull/2123) --- tools/props/Versions.props | 4 ++-- tools/specs/Microsoft.Data.SqlClient.nuspec | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index ada3af2c63..769faaa1f1 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -20,7 +20,7 @@ - 5.1.0 + 5.1.1 @@ -35,7 +35,7 @@ 5.0.0 - 5.1.0 + 5.1.1 6.0.1 1.0.0 6.0.0 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 0c56f9f8c4..0f9a5158d1 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 - + @@ -39,7 +39,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -54,7 +54,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -71,7 +71,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + From b9f133b659fe83ae19910caab4680b3cdfb9aac6 Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Mon, 16 Oct 2023 21:59:22 +0000 Subject: [PATCH 10/76] Merged PR 4036: [5.1.2] Fix | LocalDb and managed SNI (#2129) Ports [#2129](https://github.com/dotnet/SqlClient/pull/2129) --- .../Data/SqlClient/SNI/LocalDB.Windows.cs | 10 +++- .../Microsoft/Data/SqlClient/SNI/SNICommon.cs | 22 +++++---- .../Microsoft/Data/SqlClient/SNI/SNIProxy.cs | 21 ++++++--- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 15 +++--- .../netcore/src/Resources/Strings.Designer.cs | 22 ++++++++- .../netcore/src/Resources/Strings.resx | 12 +++-- .../ManualTests/DataCommon/DataTestUtility.cs | 8 +++- .../SQL/LocalDBTest/LocalDBTest.cs | 46 ++++++++++++++++++- 8 files changed, 119 insertions(+), 37 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs index 68eaa25486..a81fb89dfb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs @@ -50,8 +50,14 @@ private string GetConnectionString(string localDbInstance) { StringBuilder localDBConnectionString = new StringBuilder(MAX_LOCAL_DB_CONNECTION_STRING_SIZE + 1); int sizeOfbuffer = localDBConnectionString.Capacity; - localDBStartInstanceFunc(localDbInstance, 0, localDBConnectionString, ref sizeOfbuffer); - return localDBConnectionString.ToString(); + int result = localDBStartInstanceFunc(localDbInstance, 0, localDBConnectionString, ref sizeOfbuffer); + if (result != TdsEnums.SNI_SUCCESS) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBErrorCode, Strings.SNI_ERROR_50); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Unsuccessful 'LocalDBStartInstance' method call with {0} result to start '{1}' localDb instance", args0: result, args1: localDbInstance); + localDBConnectionString = null; + } + return localDBConnectionString?.ToString(); } internal enum LocalDBErrorState diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs index b92746098f..08272596d8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs @@ -21,16 +21,18 @@ namespace Microsoft.Data.SqlClient.SNI /// internal enum SNIProviders { - HTTP_PROV, // HTTP Provider - NP_PROV, // Named Pipes Provider - SESSION_PROV, // Session Provider - SIGN_PROV, // Sign Provider - SM_PROV, // Shared Memory Provider - SMUX_PROV, // SMUX Provider - SSL_PROV, // SSL Provider - TCP_PROV, // TCP Provider - MAX_PROVS, // Number of providers - INVALID_PROV // SQL Network Interfaces + HTTP_PROV = 0, // HTTP Provider + NP_PROV = 1, // Named Pipes Provider + SESSION_PROV = 2, // Session Provider + SIGN_PROV = 3, // Sign Provider + SM_PROV = 4, // Shared Memory Provider + SMUX_PROV = 5, // SMUX Provider + SSL_PROV = 6, // SSL Provider + TCP_PROV = 7, // TCP Provider + VIA_PROV = 8, // Virtual Interface Architecture Provider + CTAIP_PROV = 9, + MAX_PROVS = 10, // Number of providers + INVALID_PROV = 11 // SQL Network Interfaces } /// 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 ac4d3599dd..834050b4bb 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 @@ -190,7 +190,7 @@ internal static SNIHandle CreateConnectionHandle( case DataSource.Protocol.TCP: sniHandle = CreateTcpHandle(details, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo, tlsFirst, hostNameInCertificate, serverCertificateFilename); - break; + break; case DataSource.Protocol.NP: sniHandle = CreateNpHandle(details, timerExpire, parallel, tlsFirst); break; @@ -392,7 +392,7 @@ private static string GetLocalDBDataSource(string fullServerName, out bool error Debug.Assert(!string.IsNullOrWhiteSpace(localDBInstance), "Local DB Instance name cannot be empty."); localDBConnectionString = LocalDB.GetLocalDBConnectionString(localDBInstance); - if (fullServerName == null) + if (fullServerName == null || string.IsNullOrEmpty(localDBConnectionString)) { // The Last error is set in LocalDB.GetLocalDBConnectionString. We don't need to set Last here. error = true; @@ -522,7 +522,18 @@ internal static string GetLocalDBInstance(string dataSource, out bool error) ReadOnlySpan input = dataSource.AsSpan().TrimStart(); error = false; // NetStandard 2.0 does not support passing a string to ReadOnlySpan - if (input.StartsWith(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) + int index = input.IndexOf(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase); + if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) + { + instanceName = input.Trim().ToString(); + } + else if (index > 0) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.ErrorLocatingServerInstance, Strings.SNI_ERROR_26); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIProxy), EventType.ERR, "Incompatible use of prefix with LocalDb: '{0}'", dataSource); + error = true; + } + else if (index == 0) { // When netcoreapp support for netcoreapp2.1 is dropped these slice calls could be converted to System.Range\System.Index // Such ad input = input[1..]; @@ -541,10 +552,6 @@ internal static string GetLocalDBInstance(string dataSource, out bool error) error = true; } } - else if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) - { - instanceName = input.Trim().ToString(); - } return instanceName; } 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 ba650cc932..e9d5ee8829 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 @@ -1516,20 +1516,17 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj) } else { - if (TdsParserStateObjectFactory.UseManagedSNI) { - // SNI error. Append additional error message info if available. - // + // SNI error. Append additional error message info if available and hasn't been included. string sniLookupMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber); - errorMessage = (errorMessage != string.Empty) ? - (sniLookupMessage + ": " + errorMessage) : - sniLookupMessage; + errorMessage = (string.IsNullOrEmpty(errorMessage) || errorMessage.Contains(sniLookupMessage)) + ? sniLookupMessage + : (sniLookupMessage + ": " + errorMessage); } else { // SNI error. Replace the entire message. - // errorMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber); // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code @@ -1538,6 +1535,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj) errorMessage += LocalDBAPI.GetLocalDBMessage((int)details.nativeError); win32ErrorCode = 0; } + SqlClientEventSource.Log.TryAdvancedTraceEvent(" Extracting the latest exception from native SNI. errorMessage: {0}", errorMessage); } } errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})", @@ -12545,8 +12543,7 @@ internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsPar return true; // No data } - Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), - "Out of sync plp read request"); + Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), "Out of sync plp read request"); Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!"); charsLeft = len; 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 ccbc9db180..21e213b262 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -1941,6 +1941,24 @@ internal static string SNI_PN1 { } } + /// + /// Looks up a localized string similar to . + /// + internal static string SNI_PN10 { + get { + return ResourceManager.GetString("SNI_PN10", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SQL Network Interfaces. + /// + internal static string SNI_PN11 { + get { + return ResourceManager.GetString("SNI_PN11", resourceCulture); + } + } + /// /// Looks up a localized string similar to Session Provider. /// @@ -1996,7 +2014,7 @@ internal static string SNI_PN7 { } /// - /// Looks up a localized string similar to . + /// Looks up a localized string similar to VIA Provider. /// internal static string SNI_PN8 { get { @@ -2005,7 +2023,7 @@ internal static string SNI_PN8 { } /// - /// Looks up a localized string similar to SQL Network Interfaces. + /// Looks up a localized string similar to CTAIP Provider. /// internal static string SNI_PN9 { get { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index f268baa2de..b9bbac1567 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1195,10 +1195,10 @@ TCP Provider - + VIA Provider - SQL Network Interfaces + CTAIP Provider .database.windows.net @@ -1941,4 +1941,10 @@ Encrypt=Strict is not supported when targeting .NET Standard 2.0. Use .NET Standard 2.1, .NET Framework, or .NET. - + + + + + SQL Network Interfaces + + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index b6a7cff18f..37e83cbd98 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -345,8 +345,12 @@ public static bool IsAKVSetupAvailable() public static bool IsNotUsingManagedSNIOnWindows() => !UseManagedSNIOnWindows; - public static bool IsUsingNativeSNI() => !IsUsingManagedSNI(); - + public static bool IsUsingNativeSNI() => +#if !NETFRAMEWORK + DataTestUtility.IsNotUsingManagedSNIOnWindows(); +#else + true; +#endif // Synapse: UTF8 collations are not supported with Azure Synapse. // Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/40103791-utf-8-collations-should-be-supported-in-azure-syna public static bool IsUTF8Supported() 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 0c06e6cf47..60e9b293f3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs @@ -16,6 +16,7 @@ private enum InfoType state } private static bool IsLocalDBEnvironmentSet() => DataTestUtility.IsLocalDBInstalled(); + private static bool IsNativeSNI() => DataTestUtility.IsUsingNativeSNI(); private static bool IsLocalDbSharedInstanceSet() => DataTestUtility.IsLocalDbSharedInstanceSetup(); private static readonly string s_localDbConnectionString = @$"server=(localdb)\{DataTestUtility.LocalDbAppName}"; private static readonly string[] s_sharedLocalDbInstances = new string[] { @$"server=(localdb)\.\{DataTestUtility.LocalDbSharedInstanceName}", @$"server=(localdb)\." }; @@ -123,6 +124,47 @@ public static void LocalDBNamepipeMarsTest() #endregion + #region Failures + // ToDo: After adding shared memory support on managed SNI, the IsNativeSNI could be taken out + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [ConditionalTheory(nameof(IsLocalDBEnvironmentSet), nameof(IsNativeSNI))] + [InlineData("lpc:")] + public static void SharedMemoryAndSqlLocalDbConnectionTest(string prefix) + { + SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString); + stringBuilder.DataSource = prefix + stringBuilder.DataSource; + SqlException ex = Assert.Throws(() => ConnectionTest(stringBuilder.ConnectionString)); + Assert.Contains("A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 41 - Cannot open a Shared Memory connection to a remote SQL server)", ex.Message); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [InlineData("tcp:")] + [InlineData("np:")] + [InlineData("undefinded:")] + [ConditionalTheory(nameof(IsLocalDBEnvironmentSet))] + public static void PrefixAndSqlLocalDbConnectionTest(string prefix) + { + SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString); + stringBuilder.DataSource = prefix + stringBuilder.DataSource; + SqlException ex = Assert.Throws(() => ConnectionTest(stringBuilder.ConnectionString)); + Assert.Contains("A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)", ex.Message); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] + public static void InvalidSqlLocalDbConnectionTest() + { + SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString); + stringBuilder.DataSource = stringBuilder.DataSource + "Invalid123"; + SqlException ex = Assert.Throws(() => ConnectionTest(stringBuilder.ConnectionString)); + Assert.Contains("A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 50 - Local Database Runtime error occurred.", ex.Message); + if (IsNativeSNI()) + { + Assert.Contains("The specified LocalDB instance does not exist.", ex.Message); + } + } + #endregion + private static void ConnectionWithMarsTest(string connectionString) { SqlConnectionStringBuilder builder = new(connectionString) @@ -178,13 +220,13 @@ private static void RestartLocalDB() { string state = ExecuteLocalDBCommandProcess(s_commandPrompt, s_sqlLocalDbInfo, InfoType.state); int count = 5; - while (state.Equals("stopped", StringComparison.InvariantCultureIgnoreCase) && count>0) + while (state.Equals("stopped", StringComparison.InvariantCultureIgnoreCase) && count > 0) { count--; state = ExecuteLocalDBCommandProcess(s_commandPrompt, s_startLocalDbCommand, InfoType.state); Thread.Sleep(2000); } - if(state == null || state != "Running") + if (state == null || state != "Running") { throw new LocalDBNotStartedException(); } From 0df83f41b88f9182dc52102fb7505cbc6e0cc978 Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Mon, 16 Oct 2023 23:11:27 +0000 Subject: [PATCH 11/76] Merged PR 4031: [5.1.2] | Fix access violation when using Express user instances (#2101) Ports [#2101](https://github.com/dotnet/SqlClient/pull/2101) --- .../src/Microsoft/Data/SqlClient/SqlConnectionString.cs | 1 + .../tests/FunctionalTests/SqlConnectionTest.cs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 10e5fb6697..c0f16fde3d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -703,6 +703,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS _attestationProtocol = connectionOptions._attestationProtocol; _serverSPN = connectionOptions._serverSPN; _failoverPartnerSPN = connectionOptions._failoverPartnerSPN; + _hostNameInCertificate = connectionOptions._hostNameInCertificate; #if NETFRAMEWORK _connectionReset = connectionOptions._connectionReset; _contextConnection = connectionOptions._contextConnection; diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs index 13fc5c22de..669e5b611d 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs @@ -945,6 +945,15 @@ public void Open_ConnectionString_Whitespace() Assert.NotNull(ex.Message); } + [Fact] + public void Open_ConnectionString_UserInstance() + { + SqlConnection cn = new SqlConnection("User Instance=true;"); + SqlException ex = Assert.Throws(() => cn.Open()); + // Throws without access violation + Assert.NotNull(ex.Message); + } + [Fact] public void ServerVersion_Connection_Closed() { From c5dc895cf1bab6d875adaf52bf8a7c586183ba55 Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Mon, 16 Oct 2023 23:24:09 +0000 Subject: [PATCH 12/76] Merged PR 4039: [5.1.2] | Fix .NET and .NET standard file version (#2093) Ports [#2093](https://github.com/dotnet/SqlClient/pull/2093) --- tools/props/Versions.props | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 769faaa1f1..e3a0355956 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -1,12 +1,15 @@ - 1.0.0.0 + 5.1.0 + 0 + $(MdsVersionDefault).$(BuildNumber) + $(AssemblyFileVersion) 5.0.0.0 - 5.1.0-dev + $(MdsVersionDefault)-dev $(NugetPackageVersion) From f85aa557ce664b2adece39363e1b50c7273ca82f Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Tue, 17 Oct 2023 03:14:19 +0000 Subject: [PATCH 13/76] Merged PR 4029: [5.1.2] Fix | Adding non-string support to SqlConnectionStringBuilder property indexer Ports [#2018](https://github.com/dotnet/SqlClient/pull/2018) --- .../Data/Common/DbConnectionStringCommon.cs | 8 +++++ .../SqlClient/SqlConnectionEncryptOption.cs | 5 +++ .../SqlConnectionStringBuilderTest.cs | 32 +++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index f8a31231c0..7e21441852 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -816,10 +816,18 @@ internal static SqlConnectionEncryptOption ConvertToSqlConnectionEncryptOption(s { return DbConnectionStringDefaults.Encrypt; } + else if(value is SqlConnectionEncryptOption eValue) + { + return eValue; + } else if (value is string sValue) { return SqlConnectionEncryptOption.Parse(sValue); } + else if(value is bool bValue) + { + return SqlConnectionEncryptOption.Parse(bValue); + } throw ADP.InvalidConnectionOptionValue(keyword); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs index cb5b467298..997833437f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs @@ -45,6 +45,11 @@ public static SqlConnectionEncryptOption Parse(string value) } } + internal static SqlConnectionEncryptOption Parse(bool value) + { + return value ? Mandatory : Optional; + } + /// public static bool TryParse(string value, out SqlConnectionEncryptOption result) { diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs index 7e65cec996..557833e80c 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs @@ -260,7 +260,7 @@ public void SetInvalidPacketSize_Throws(int invalid) } [Theory] - [InlineData("AttachDBFilename","somefile.db")] + [InlineData("AttachDBFilename", "somefile.db")] public void SetKeyword(string keyword, string value) { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(); @@ -387,7 +387,7 @@ public void ConnectionBuilderEncryptBackwardsCompatibility() builder.Encrypt = false; Assert.Equal("Encrypt=False", builder.ConnectionString); Assert.False(builder.Encrypt); - + builder.Encrypt = true; Assert.Equal("Encrypt=True", builder.ConnectionString); Assert.True(builder.Encrypt); @@ -409,6 +409,18 @@ public void ConnectionBuilderEncryptBackwardsCompatibility() Assert.True(builder.Encrypt); } + [Fact] + public void EncryptParserValidValuesPropertyIndexerForEncryptionOption() + { + SqlConnectionStringBuilder builder = new(); + builder["Encrypt"] = SqlConnectionEncryptOption.Strict; + CheckEncryptType(builder, SqlConnectionEncryptOption.Strict); + builder["Encrypt"] = SqlConnectionEncryptOption.Optional; + CheckEncryptType(builder, SqlConnectionEncryptOption.Optional); + builder["Encrypt"] = SqlConnectionEncryptOption.Mandatory; + CheckEncryptType(builder, SqlConnectionEncryptOption.Mandatory); + } + [Theory] [InlineData("true", "True")] [InlineData("mandatory", "True")] @@ -420,6 +432,16 @@ public void ConnectionBuilderEncryptBackwardsCompatibility() public void EncryptParserValidValuesParsesSuccessfully(string value, string expectedValue) => Assert.Equal(expectedValue, SqlConnectionEncryptOption.Parse(value).ToString()); + [Theory] + [InlineData(true)] + [InlineData(false)] + public void EncryptParserValidValuesPropertyIndexerForBoolean(bool value) + { + SqlConnectionStringBuilder builder = new(); + builder["Encrypt"] = value; + CheckEncryptType(builder, value ? SqlConnectionEncryptOption.Mandatory : SqlConnectionEncryptOption.Optional); + } + [Theory] [InlineData("something")] [InlineData("")] @@ -627,5 +649,11 @@ internal void ExecuteConnectionStringTests(string connectionString) Assert.NotNull(connection); } } + + internal static void CheckEncryptType(SqlConnectionStringBuilder builder, SqlConnectionEncryptOption expectedValue) + { + Assert.IsType(builder.Encrypt); + Assert.Equal(expectedValue, builder.Encrypt); + } } } From 7ad5c58dbf1bedfd6bf1d322ef7891149a897e52 Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Tue, 17 Oct 2023 17:30:59 +0000 Subject: [PATCH 14/76] Merged PR 4038: [5.1.2] Fix | AE enclave retry logic not working for async queries (#1988) Ports [#1988](https://github.com/dotnet/SqlClient/pull/1988) --- .../SqlColumnEncryptionEnclaveProvider.xml | 13 +- ...umnEncryptionEnclaveProvider.NetCoreApp.cs | 4 +- .../SqlColumnEncryptionEnclaveProvider.cs | 2 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 133 ++++++++++++------ .../SqlColumnEncryptionEnclaveProvider.cs | 2 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 104 +++++++++----- .../AzureAttestationBasedEnclaveProvider.cs | 4 +- .../Data/SqlClient/EnclaveDelegate.Crypto.cs | 17 ++- .../SqlClient/EnclaveDelegate.NotSupported.cs | 9 +- .../Data/SqlClient/EnclaveProviderBase.cs | 6 +- .../NoneAttestationEnclaveProvider.cs | 4 +- .../VirtualSecureModeEnclaveProviderBase.cs | 4 +- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 86 +++++++++++ .../SystemDataInternals/CommandHelper.cs | 17 +++ 14 files changed, 294 insertions(+), 111 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionEnclaveProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionEnclaveProvider.xml index 261f5e57ed..dfbdbe8782 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionEnclaveProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionEnclaveProvider.xml @@ -20,8 +20,8 @@ the enclave attestation protocol as well as the logic for creating and caching e The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. A Diffie-Hellman algorithm object that encapsulates a client-side key pair. The set of parameters required for an enclave session. - The set of extra data needed for attestating the enclave. - The length of the extra data needed for attestating the enclave. + The set of extra data needed for attesting the enclave. + The length of the extra data needed for attesting the enclave. The requested enclave session or if the provider doesn't implement session caching. A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. 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. @@ -29,8 +29,8 @@ the enclave attestation protocol as well as the logic for creating and caching e The endpoint of an attestation service for attesting the enclave. - A set of extra data needed for attestating the enclave. - The length of the extra data needed for attestating the enclave. + A set of extra data needed for attesting the enclave. + The length of the extra data needed for attesting the enclave. Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. To be added. @@ -38,10 +38,11 @@ the enclave attestation protocol as well as the logic for creating and caching e The set of parameters required for enclave session. to indicate that a set of extra data needs to be generated for attestation; otherwise, . + Indicates if this is a retry from a failed call. When this method returns, the requested enclave session or if the provider doesn't implement session caching. This parameter is treated as uninitialized. A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. - A set of extra data needed for attestating the enclave. - The length of the extra data needed for attestating the enclave. + A set of extra data needed for attesting the enclave. + The length of the extra data needed for attesting the enclave. 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 in the parameter. To be added. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs index cca04fc323..fd81db557d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs @@ -15,8 +15,8 @@ internal abstract partial class SqlColumnEncryptionEnclaveProvider /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. /// A Diffie-Hellman algorithm object encapsulating a client-side key pair. /// The set of parameters required for enclave session. - /// The set of extra data needed for attestating the enclave. - /// The length of the extra data needed for attestating the enclave. + /// The set of extra data needed for attesting the enclave. + /// The length of the extra data needed for attesting the enclave. /// The requested enclave session or null if the provider does not implement session caching. /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellman clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs index e374d43665..9223702509 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs @@ -8,7 +8,7 @@ namespace Microsoft.Data.SqlClient internal abstract partial class SqlColumnEncryptionEnclaveProvider { /// - internal abstract void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength); + internal abstract void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength); /// internal abstract SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength); 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 b576ef3510..f3b26e7899 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 @@ -137,6 +137,11 @@ protected override void AfterCleared(SqlCommand owner) /// Internal flag for testing purposes that forces all queries to internally end async calls. /// private static bool _forceInternalEndQuery = false; + + /// + /// Internal flag for testing purposes that forces one RetryableEnclaveQueryExecutionException during GenerateEnclavePackage + /// + private static bool _forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage = false; #endif private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); @@ -2221,7 +2226,7 @@ private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncC // back into pool when we should not. } - bool usedCache; + bool usedCache = false; Task writeTask = null; try { @@ -2238,7 +2243,10 @@ private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncC // For async, RunExecuteReader will never put the stateObj back into the pool, so do so now. ReliablePutStateObject(); - throw; + if (inRetry || e is not EnclaveDelegate.RetryableEnclaveQueryExecutionException) + { + throw; + } } if (writeTask != null) @@ -2439,12 +2447,7 @@ long firstAttemptStart // Remove the entry from the cache since it was inconsistent. SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); - if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) - { - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, - enclaveSessionParameters, this.enclavePackage.EnclaveSession); - } + InvalidateEnclaveSession(); try { @@ -2480,6 +2483,26 @@ long firstAttemptStart }, TaskScheduler.Default); } + private void InvalidateEnclaveSession() + { + if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) + { + EnclaveDelegate.Instance.InvalidateEnclaveSession( + this._activeConnection.AttestationProtocol, + this._activeConnection.Parser.EnclaveType, + GetEnclaveSessionParameters(), + this.enclavePackage.EnclaveSession); + } + } + + private EnclaveSessionParameters GetEnclaveSessionParameters() + { + return new EnclaveSessionParameters( + this._activeConnection.DataSource, + this._activeConnection.EnclaveAttestationUrl, + this._activeConnection.Database); + } + private void BeginExecuteReaderInternalReadStage(TaskCompletionSource completion) { Debug.Assert(completion != null, "CompletionSource should not be null"); @@ -3660,7 +3683,13 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r try { // Fetch the encryption information that applies to any of the input parameters. - describeParameterEncryptionDataReader = TryFetchInputParameterEncryptionInfo(timeout, isAsync, asyncWrite, out describeParameterEncryptionNeeded, out fetchInputParameterEncryptionInfoTask, out describeParameterEncryptionRpcOriginalRpcMap); + describeParameterEncryptionDataReader = TryFetchInputParameterEncryptionInfo(timeout, + isAsync, + asyncWrite, + out describeParameterEncryptionNeeded, + out fetchInputParameterEncryptionInfoTask, + out describeParameterEncryptionRpcOriginalRpcMap, + inRetry); Debug.Assert(describeParameterEncryptionNeeded || describeParameterEncryptionDataReader == null, "describeParameterEncryptionDataReader should be null if we don't need to request describe parameter encryption request."); @@ -3695,7 +3724,13 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r // Mark that we should not process the finally block since we have async execution pending. // Note that this should be done outside the task's continuation delegate. processFinallyBlock = false; - describeParameterEncryptionDataReader = GetParameterEncryptionDataReader(out returnTask, fetchInputParameterEncryptionInfoTask, describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap, describeParameterEncryptionNeeded); + describeParameterEncryptionDataReader = GetParameterEncryptionDataReader( + out returnTask, + fetchInputParameterEncryptionInfoTask, + describeParameterEncryptionDataReader, + describeParameterEncryptionRpcOriginalRpcMap, + describeParameterEncryptionNeeded, + inRetry); decrementAsyncCountInFinallyBlock = false; } @@ -3707,14 +3742,22 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r // Mark that we should not process the finally block since we have async execution pending. // Note that this should be done outside the task's continuation delegate. processFinallyBlock = false; - describeParameterEncryptionDataReader = GetParameterEncryptionDataReaderAsync(out returnTask, describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap, describeParameterEncryptionNeeded); + describeParameterEncryptionDataReader = GetParameterEncryptionDataReaderAsync( + out returnTask, + describeParameterEncryptionDataReader, + describeParameterEncryptionRpcOriginalRpcMap, + describeParameterEncryptionNeeded, + inRetry); decrementAsyncCountInFinallyBlock = false; } else { // For synchronous execution, read the results of describe parameter encryption here. - ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); + ReadDescribeEncryptionParameterResults( + describeParameterEncryptionDataReader, + describeParameterEncryptionRpcOriginalRpcMap, + inRetry); } #if DEBUG @@ -3761,7 +3804,7 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r private SqlDataReader GetParameterEncryptionDataReader(out Task returnTask, Task fetchInputParameterEncryptionInfoTask, SqlDataReader describeParameterEncryptionDataReader, - ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, bool describeParameterEncryptionNeeded) + ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, bool describeParameterEncryptionNeeded, bool inRetry) { returnTask = AsyncHelper.CreateContinuationTaskWithState(fetchInputParameterEncryptionInfoTask, this, (object state) => @@ -3791,7 +3834,7 @@ private SqlDataReader GetParameterEncryptionDataReader(out Task returnTask, Task Debug.Assert(null == command._stateObj, "non-null state object in PrepareForTransparentEncryption."); // Read the results of describe parameter encryption. - command.ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); + command.ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap, inRetry); #if DEBUG // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. @@ -3836,7 +3879,7 @@ private SqlDataReader GetParameterEncryptionDataReader(out Task returnTask, Task private SqlDataReader GetParameterEncryptionDataReaderAsync(out Task returnTask, SqlDataReader describeParameterEncryptionDataReader, - ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, bool describeParameterEncryptionNeeded) + ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, bool describeParameterEncryptionNeeded, bool inRetry) { returnTask = Task.Run(() => { @@ -3866,7 +3909,7 @@ private SqlDataReader GetParameterEncryptionDataReaderAsync(out Task returnTask, // Read the results of describe parameter encryption. ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, - describeParameterEncryptionRpcOriginalRpcMap); + describeParameterEncryptionRpcOriginalRpcMap, inRetry); #if DEBUG // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. if (_sleepAfterReadDescribeEncryptionParameterResults) @@ -3903,13 +3946,15 @@ private SqlDataReader GetParameterEncryptionDataReaderAsync(out Task returnTask, /// /// /// + /// Indicates if this is a retry from a failed call. /// private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, bool isAsync, bool asyncWrite, out bool inputParameterEncryptionNeeded, out Task task, - out ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap) + out ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, + bool isRetry) { inputParameterEncryptionNeeded = false; task = null; @@ -3921,10 +3966,10 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); + EnclaveSessionParameters enclaveSessionParameters = GetEnclaveSessionParameters(); SqlEnclaveSession sqlEnclaveSession = null; - EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, true, out sqlEnclaveSession, out customData, out customDataLength); + EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, true, isRetry, out sqlEnclaveSession, out customData, out customDataLength); if (sqlEnclaveSession == null) { enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType, enclaveSessionParameters.AttestationUrl, customData, customDataLength); @@ -4170,7 +4215,8 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques /// /// Resultset from calling to sp_describe_parameter_encryption /// Readonly dictionary with the map of parameter encryption rpc requests with the corresponding original rpc requests. - private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap) + /// Indicates if this is a retry from a failed call. + private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, bool isRetry) { _SqlRPC rpc = null; int currentOrdinal = -1; @@ -4449,9 +4495,16 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); - EnclaveDelegate.Instance.CreateEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, attestationInfo, enclaveAttestationParameters, customData, customDataLength); + EnclaveDelegate.Instance.CreateEnclaveSession( + attestationProtocol, + enclaveType, + GetEnclaveSessionParameters(), + attestationInfo, + enclaveAttestationParameters, + customData, + customDataLength, + isRetry); enclaveAttestationParameters = null; attestationInfoRead = true; } @@ -4552,7 +4605,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { - if (inRetry || isAsync) + if (inRetry) { throw; } @@ -4561,22 +4614,16 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior // First invalidate the entry from the cache, so that we refresh our encryption MD. SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); - if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) - { - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, - enclaveSessionParameters, this.enclavePackage.EnclaveSession); - } + InvalidateEnclaveSession(); - return RunExecuteReader(cmdBehavior, runBehavior, returnStream, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, isAsync, inRetry: true, method: method); + return RunExecuteReader(cmdBehavior, runBehavior, returnStream, completion, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, isAsync, inRetry: true, method: method); } catch (SqlException ex) { // We only want to retry once, so don't retry if we are already in retry. // If we didn't use the cache, we don't want to retry. - // The async retried are handled separately, handle only sync calls here. - if (inRetry || isAsync || (!usedCache && !ShouldUseEnclaveBasedWorkflow)) + if (inRetry || (!usedCache && !ShouldUseEnclaveBasedWorkflow)) { throw; } @@ -4605,14 +4652,9 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior // First invalidate the entry from the cache, so that we refresh our encryption MD. SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); - if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) - { - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, - enclaveSessionParameters, this.enclavePackage.EnclaveSession); - } + InvalidateEnclaveSession(); - return RunExecuteReader(cmdBehavior, runBehavior, returnStream, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, isAsync, inRetry: true, method: method); + return RunExecuteReader(cmdBehavior, runBehavior, returnStream, completion, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, isAsync, inRetry: true, method: method); } } } @@ -4712,9 +4754,15 @@ private void GenerateEnclavePackage() try { - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); +#if DEBUG + if (_forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage) + { + _forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage = false; + throw new EnclaveDelegate.RetryableEnclaveQueryExecutionException("testing", null); + } +#endif this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(attestationProtocol, keysToBeSentToEnclave, - this.CommandText, enclaveType, enclaveSessionParameters, _activeConnection, this); + this.CommandText, enclaveType, GetEnclaveSessionParameters(), _activeConnection, this); } catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { @@ -4772,8 +4820,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi bool processFinallyBlock = true; bool decrementAsyncCountOnFailure = false; - // If we are in retry, don't increment the Async count. This should have already been set. - if (isAsync && !inRetry) + if (isAsync) { _activeConnection.GetOpenTdsConnection().IncrementAsyncCount(); decrementAsyncCountOnFailure = true; 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 9017b84717..de26b79232 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 @@ -11,7 +11,7 @@ internal abstract class SqlColumnEncryptionEnclaveProvider { /// - internal abstract void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength); + internal abstract void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength); /// internal abstract SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength); 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 fdfa0772ea..6922be9735 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 @@ -83,6 +83,11 @@ public sealed class SqlCommand : DbCommand, ICloneable /// Internal flag for testing purposes that forces all queries to internally end async calls. /// private static bool _forceInternalEndQuery = false; + + /// + /// Internal flag for testing purposes that forces one RetryableEnclaveQueryExecutionException during GenerateEnclavePackage + /// + private static bool _forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage = false; #endif internal static readonly Action s_cancelIgnoreFailure = CancelIgnoreFailureCallback; @@ -2649,7 +2654,7 @@ private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncC // back into pool when we should not. } - bool usedCache; + bool usedCache = false; Task writeTask = null; try { @@ -2782,11 +2787,7 @@ private bool TriggerInternalEndAndRetryIfNecessary(CommandBehavior behavior, obj // Remove the enrty from the cache since it was inconsistent. SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); - if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) - { - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, enclaveSessionParameters, this.enclavePackage.EnclaveSession); - } + InvalidateEnclaveSession(); try { @@ -2827,6 +2828,26 @@ private bool TriggerInternalEndAndRetryIfNecessary(CommandBehavior behavior, obj } } + private void InvalidateEnclaveSession() + { + if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) + { + EnclaveDelegate.Instance.InvalidateEnclaveSession( + this._activeConnection.AttestationProtocol, + this._activeConnection.Parser.EnclaveType, + GetEnclaveSessionParameters(), + this.enclavePackage.EnclaveSession); + } + } + + private EnclaveSessionParameters GetEnclaveSessionParameters() + { + return new EnclaveSessionParameters( + this._activeConnection.DataSource, + this._activeConnection.EnclaveAttestationUrl, + this._activeConnection.Database); + } + private void BeginExecuteReaderInternalReadStage(TaskCompletionSource completion) { Debug.Assert(completion != null, "CompletionSource should not be null"); @@ -4143,7 +4164,8 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r asyncWrite, out describeParameterEncryptionNeeded, out fetchInputParameterEncryptionInfoTask, - out describeParameterEncryptionRpcOriginalRpcMap); + out describeParameterEncryptionRpcOriginalRpcMap, + inRetry); Debug.Assert(describeParameterEncryptionNeeded || describeParameterEncryptionDataReader == null, "describeParameterEncryptionDataReader should be null if we don't need to request describe parameter encryption request."); @@ -4210,7 +4232,10 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); // Read the results of describe parameter encryption. - ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); + ReadDescribeEncryptionParameterResults( + describeParameterEncryptionDataReader, + describeParameterEncryptionRpcOriginalRpcMap, + inRetry); #if DEBUG // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. @@ -4295,7 +4320,7 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); // Read the results of describe parameter encryption. - ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); + ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap, inRetry); #if DEBUG // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. if (_sleepAfterReadDescribeEncryptionParameterResults) @@ -4332,7 +4357,7 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r else { // For synchronous execution, read the results of describe parameter encryption here. - ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); + ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap, inRetry); } #if DEBUG @@ -4410,13 +4435,15 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r /// /// /// + /// Indicates if this is a retry from a failed call. /// private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, bool async, bool asyncWrite, out bool inputParameterEncryptionNeeded, out Task task, - out ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap) + out ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, + bool inRetry) { inputParameterEncryptionNeeded = false; task = null; @@ -4428,10 +4455,10 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); + EnclaveSessionParameters enclaveSessionParameters = GetEnclaveSessionParameters(); SqlEnclaveSession sqlEnclaveSession = null; - EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, true, out sqlEnclaveSession, out customData, out customDataLength); + EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, true, inRetry, out sqlEnclaveSession, out customData, out customDataLength); if (sqlEnclaveSession == null) { enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType, enclaveSessionParameters.AttestationUrl, customData, customDataLength); @@ -4691,7 +4718,8 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques /// /// Resultset from calling to sp_describe_parameter_encryption /// Readonly dictionary with the map of parameter encryption rpc requests with the corresponding original rpc requests. - private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap) + /// Indicates if this is a retry from a failed call. + private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, bool inRetry) { _SqlRPC rpc = null; int currentOrdinal = -1; @@ -4966,9 +4994,16 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); - EnclaveDelegate.Instance.CreateEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, attestationInfo, enclaveAttestationParameters, customData, customDataLength); + EnclaveDelegate.Instance.CreateEnclaveSession( + attestationProtocol, + enclaveType, + GetEnclaveSessionParameters(), + attestationInfo, + enclaveAttestationParameters, + customData, + customDataLength, + inRetry); enclaveAttestationParameters = null; attestationInfoRead = true; } @@ -5090,7 +5125,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { - if (inRetry || async) + if (inRetry) { throw; } @@ -5099,22 +5134,16 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior // First invalidate the entry from the cache, so that we refresh our encryption MD. SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); - if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) - { - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, - enclaveSessionParameters, this.enclavePackage.EnclaveSession); - } + InvalidateEnclaveSession(); - return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true); + return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, completion, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true); } catch (SqlException ex) { // We only want to retry once, so don't retry if we are already in retry. // If we didn't use the cache, we don't want to retry. - // The async retried are handled separately, handle only sync calls here. - if (inRetry || async || (!usedCache && !ShouldUseEnclaveBasedWorkflow)) + if (inRetry || (!usedCache && !ShouldUseEnclaveBasedWorkflow)) { throw; } @@ -5143,14 +5172,9 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior // First invalidate the entry from the cache, so that we refresh our encryption MD. SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); - if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) - { - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, - enclaveSessionParameters, this.enclavePackage.EnclaveSession); - } + InvalidateEnclaveSession(); - return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true); + return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, completion, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true); } } } @@ -5158,7 +5182,6 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior { return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async, inRetry: inRetry); } - } #if DEBUG finally @@ -5273,9 +5296,15 @@ private void GenerateEnclavePackage() try { - EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); +#if DEBUG + if (_forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage) + { + _forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage = false; + throw new EnclaveDelegate.RetryableEnclaveQueryExecutionException("testing", null); + } +#endif this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(attestationProtocol, keysToBeSentToEnclave, - this.CommandText, enclaveType, enclaveSessionParameters, _activeConnection, this); + this.CommandText, enclaveType, GetEnclaveSessionParameters(), _activeConnection, this); } catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { @@ -5356,8 +5385,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi bool processFinallyBlock = true; bool decrementAsyncCountOnFailure = false; - // If we are in retry, don't increment the Async count. This should have already been set. - if (async && !inRetry) + if (async) { _activeConnection.GetOpenTdsConnection().IncrementAsyncCount(); decrementAsyncCountOnFailure = true; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 433347ef10..07848e651c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -65,9 +65,9 @@ internal class AzureAttestationEnclaveProvider : EnclaveProviderBase #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) + internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) { - GetEnclaveSessionHelper(enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength); + GetEnclaveSessionHelper(enclaveSessionParameters, generateCustomData, isRetry, 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. 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 d1e6c71527..db1bdea90f 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 @@ -20,10 +20,11 @@ internal sealed partial class EnclaveDelegate /// 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. + /// A set of extra data needed for attesting the enclave. + /// The length of the extra data needed for attesting the enclave. + /// Indicates if this is a retry from a failed call. internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, - byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, byte[] customData, int customDataLength) + byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, byte[] customData, int customDataLength, bool isRetry) { lock (_lock) { @@ -32,6 +33,7 @@ internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationP sqlColumnEncryptionEnclaveProvider.GetEnclaveSession( enclaveSessionParameters, generateCustomData: false, + isRetry: isRetry, sqlEnclaveSession: out SqlEnclaveSession sqlEnclaveSession, counter: out _, customData: out _, @@ -60,15 +62,15 @@ internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationP } } - internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) + internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) { - GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out _, out customData, out customDataLength, throwIfNull: false); + GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, generateCustomData, isRetry, 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) + private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, 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); + sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(enclaveSessionParameters, generateCustomData, isRetry, out sqlEnclaveSession, out counter, out customData, out customDataLength); if (throwIfNull && sqlEnclaveSession == null) { @@ -143,6 +145,7 @@ internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol enclaveType, enclaveSessionParameters, generateCustomData: false, + isRetry: false, sqlEnclaveSession: out sqlEnclaveSession, counter: out counter, customData: out _, 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 7b43c8cb66..461bfdf6d7 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 @@ -24,15 +24,16 @@ internal byte[] GetSerializedAttestationParameters( /// 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. + /// A set of extra data needed for attesting the enclave. + /// The length of the extra data needed for attesting the enclave. + /// Indicates if this is a retry from a failed call. internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, - byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, byte[] customData, int customDataLength) + byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, byte[] customData, int customDataLength, bool isRetry) { throw new PlatformNotSupportedException(); } - internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) + internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) { throw new PlatformNotSupportedException(); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 0c72beac9d..b8a52b9e4b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -89,7 +89,7 @@ internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider #region protected methods // Helper method to get the enclave session from the cache if present - protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionParameters, bool shouldGenerateNonce, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) + protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionParameters, bool shouldGenerateNonce, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) { customData = null; customDataLength = 0; @@ -107,7 +107,7 @@ protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionPa { sameThreadRetry = true; } - else + else if (!isRetry) { // We are explicitly not signalling the event here, as we want to hold the event till driver calls CreateEnclaveSession // If we signal the event now, then multiple thread end up calling GetAttestationParameters which triggers the attestation workflow. @@ -124,7 +124,7 @@ protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionPa // In case of multi-threaded application, first thread will set the event and all the subsequent threads will wait here either until the enclave // session is created or timeout happens. - if (sessionCacheLockTaken || sameThreadRetry) + if (sessionCacheLockTaken || sameThreadRetry || isRetry) { // While the current thread is waiting for event to be signaled and in the meanwhile we already completed the attestation on different thread // then we need to signal the event here diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs index 84aea73940..fabd69c976 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -18,9 +18,9 @@ internal class NoneAttestationEnclaveProvider : EnclaveProviderBase // 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) + internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) { - GetEnclaveSessionHelper(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out customData, out customDataLength); + GetEnclaveSessionHelper(enclaveSessionParameters, false, isRetry, 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. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index c4b9987f01..d612411aa8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -86,9 +86,9 @@ internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : Enclave // 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) + internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) { - GetEnclaveSessionHelper(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out customData, out customDataLength); + GetEnclaveSessionHelper(enclaveSessionParameters, false, isRetry, 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. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 945badebb1..bd6060c179 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -2390,6 +2390,92 @@ public void TestRetryWhenAEParameterMetadataCacheIsStale(string connectionString cmd.ExecuteNonQuery(); } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.EnclaveEnabled))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestRetryWhenAEEnclaveCacheIsStale(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(); + + // change the CEK and encryption type to randomized for the CustomerId column to ensure enclaves are used + using SqlCommand cmd = new SqlCommand( + string.Format(alterCekQueryFormatString, _tableName, table.columnEncryptionKey2.Name), + sqlConnection, + null, + SqlCommandColumnEncryptionSetting.Enabled); + cmd.ExecuteNonQuery(); + + // execute the select query to create the cache entry + cmd.CommandText = enclaveSelectQuery; + cmd.Parameters.AddWithValue("@CustomerId", 0); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + Assert.Equal(customerId, (int)reader[0]); + } + reader.Close(); + } + + CommandHelper.InvalidateEnclaveSession(cmd); + + // Execute again to exercise the session retry logic + using (SqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + Assert.Equal(customerId, (int)reader[0]); + } + reader.Close(); + } + + CommandHelper.InvalidateEnclaveSession(cmd); + + // Execute again to exercise the async session retry logic + Task readAsyncTask = ReadAsync(cmd, values, CommandBehavior.Default); + readAsyncTask.GetAwaiter().GetResult(); + +#if DEBUG + CommandHelper.ForceThrowDuringGenerateEnclavePackage(cmd); + + // Execute again to exercise the session retry logic + using (SqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + Assert.Equal(customerId, (int)reader[0]); + } + reader.Close(); + } + + CommandHelper.ForceThrowDuringGenerateEnclavePackage(cmd); + + // Execute again to exercise the async session retry logic + Task readAsyncTask2 = ReadAsync(cmd, values, CommandBehavior.Default); + readAsyncTask2.GetAwaiter().GetResult(); +#endif + + // 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/SQL/Common/SystemDataInternals/CommandHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/CommandHelper.cs index 6d56c30e49..ba3d578673 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/CommandHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/CommandHelper.cs @@ -12,6 +12,11 @@ internal static class CommandHelper private static Type s_sqlCommand = typeof(SqlCommand); private static MethodInfo s_completePendingReadWithSuccess = s_sqlCommand.GetMethod("CompletePendingReadWithSuccess", BindingFlags.NonPublic | BindingFlags.Instance); private static MethodInfo s_completePendingReadWithFailure = s_sqlCommand.GetMethod("CompletePendingReadWithFailure", BindingFlags.NonPublic | BindingFlags.Instance); + private static MethodInfo s_invalidateEnclaveSession = s_sqlCommand.GetMethod("InvalidateEnclaveSession", BindingFlags.NonPublic | BindingFlags.Instance); +#if DEBUG + private static FieldInfo s_forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage = + s_sqlCommand.GetField(@"_forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage", BindingFlags.NonPublic | BindingFlags.Static); +#endif public static PropertyInfo s_debugForceAsyncWriteDelay = s_sqlCommand.GetProperty("DebugForceAsyncWriteDelay", BindingFlags.NonPublic | BindingFlags.Static); public static FieldInfo s_sleepDuringTryFetchInputParameterEncryptionInfo = s_sqlCommand.GetField(@"_sleepDuringTryFetchInputParameterEncryptionInfo", BindingFlags.Static | BindingFlags.NonPublic); public static PropertyInfo s_isDescribeParameterEncryptionRPCCurrentlyInProgress = s_sqlCommand.GetProperty(@"IsDescribeParameterEncryptionRPCCurrentlyInProgress", BindingFlags.Instance | BindingFlags.NonPublic); @@ -31,6 +36,18 @@ internal static void CompletePendingReadWithFailure(SqlCommand command, int erro s_completePendingReadWithFailure.Invoke(command, new object[] { errorCode, resetForcePendingReadsToWait }); } + internal static void InvalidateEnclaveSession(SqlCommand command) + { + s_invalidateEnclaveSession.Invoke(command, null); + } + +#if DEBUG + internal static void ForceThrowDuringGenerateEnclavePackage(SqlCommand command) + { + s_forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage.SetValue(command, true); + } +#endif + internal static int ForceAsyncWriteDelay { get { return (int)s_debugForceAsyncWriteDelay.GetValue(null); } From 3fd439bda7ccfa76a346d8a417c9b0a268b9dcb3 Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Tue, 17 Oct 2023 18:29:32 +0000 Subject: [PATCH 15/76] Merged PR 4037: [5.1.2] | Fix activity correlator to continue using same GUID for current thread activity (#1997) Ports [#1997](https://github.com/dotnet/SqlClient/pull/1997) --- .../Data/Common/ActivityCorrelator.cs | 29 ++++--------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ActivityCorrelator.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ActivityCorrelator.cs index ef6b9b6cd2..5823921abc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ActivityCorrelator.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ActivityCorrelator.cs @@ -19,16 +19,14 @@ internal sealed class ActivityId internal readonly Guid Id; internal readonly uint Sequence; - internal ActivityId(uint sequence) + internal ActivityId(Guid? currentActivityId, uint sequence = 1) { - this.Id = Guid.NewGuid(); - this.Sequence = sequence; + Id = currentActivityId ?? Guid.NewGuid(); + Sequence = sequence; } public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "{0}:{1}", this.Id, this.Sequence); - } + => string.Format(CultureInfo.InvariantCulture, "{0}:{1}", Id, Sequence); } // Declare the ActivityId which will be stored in TLS. The Id is unique for each thread. @@ -40,27 +38,12 @@ public override string ToString() /// /// Get the current ActivityId /// - internal static ActivityId Current - { - get - { - if (t_tlsActivity == null) - { - t_tlsActivity = new ActivityId(1); - } - return t_tlsActivity; - } - } + internal static ActivityId Current => t_tlsActivity ??= new ActivityId(null); /// /// Increment the sequence number and generate the new ActivityId /// /// ActivityId - internal static ActivityId Next() - { - t_tlsActivity = new ActivityId( (t_tlsActivity?.Sequence ?? 0) + 1); - - return t_tlsActivity; - } + internal static ActivityId Next() => t_tlsActivity = new ActivityId(t_tlsActivity?.Id, (t_tlsActivity?.Sequence ?? 0) + 1); } } From 4deb800c9534992d80573bcdb39439bece4ce0d5 Mon Sep 17 00:00:00 2001 From: David Engel Date: Wed, 18 Oct 2023 23:42:01 +0000 Subject: [PATCH 16/76] Merged PR 3992: [5.1.3] --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 61 ++++++---- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 107 +++++++++++------- .../SqlConnectionBasicTests.cs | 19 ++++ .../tests/FunctionalTests/TestTdsServer.cs | 15 ++- .../tools/TDS/TDS.EndPoint/TDSServerParser.cs | 12 ++ .../TDS/TDS/PreLogin/TDSPreLoginToken.cs | 5 +- .../TDSPreLoginTokenEncryptionType.cs | 3 +- .../tests/tools/TDS/TDS/TDSEncryptionType.cs | 7 +- .../tests/tools/TDS/TDS/TDSUtilities.cs | 12 ++ 9 files changed, 169 insertions(+), 72 deletions(-) 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 e9d5ee8829..17327f16e1 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 @@ -973,6 +973,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( int payloadOffset = 0; int payloadLength = 0; int option = payload[offset++]; + bool serverSupportsEncryption = false; while (option != (byte)PreLoginOptions.LASTOPT) { @@ -996,6 +997,13 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( break; case (int)PreLoginOptions.ENCRYPT: + if (tlsFirst) + { + // Can skip/ignore this option if we are doing TDS 8. + offset += 4; + break; + } + payloadOffset = payload[offset++] << 8 | payload[offset++]; payloadLength = payload[offset++] << 8 | payload[offset++]; @@ -1009,16 +1017,11 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( LOGIN } */ + // Any response other than NOT_SUP means the server supports encryption. + serverSupportsEncryption = serverOption != EncryptionOptions.NOT_SUP; + switch (_encryptionOption) { - case (EncryptionOptions.ON): - if (serverOption == EncryptionOptions.NOT_SUP) - { - _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); - _physicalStateObj.Dispose(); - ThrowExceptionAndWarning(_physicalStateObj); - } - break; case (EncryptionOptions.OFF): if (serverOption == EncryptionOptions.OFF) { @@ -1034,8 +1037,9 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( break; case (EncryptionOptions.NOT_SUP): - if (!tlsFirst && serverOption == EncryptionOptions.REQ) + if (serverOption == EncryptionOptions.REQ) { + // Server requires encryption, but client does not support it. _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); ThrowExceptionAndWarning(_physicalStateObj); @@ -1043,22 +1047,16 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( break; default: - Debug.Fail("Invalid client encryption option detected"); + // Any other client option needs encryption + if (serverOption == EncryptionOptions.NOT_SUP) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } break; } - if (_encryptionOption == EncryptionOptions.ON || - _encryptionOption == EncryptionOptions.LOGIN) - { - // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || - (_connHandler._accessTokenInBytes != null && !trustServerCert); - uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) - | (is2005OrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); - - EnableSsl(info, encrypt, integratedSecurity, serverCert); - } - break; case (int)PreLoginOptions.INSTANCE: @@ -1139,6 +1137,25 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( } } + if (_encryptionOption == EncryptionOptions.ON || + _encryptionOption == EncryptionOptions.LOGIN) + { + if (!serverSupportsEncryption) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + + // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || + (_connHandler._accessTokenInBytes != null && !trustServerCert); + uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) + | (is2005OrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); + + EnableSsl(info, encrypt, integratedSecurity, serverCert); + } + return PreLoginHandshakeStatus.Successful; } 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 355bcf8a47..e143b13dfc 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 @@ -1392,6 +1392,8 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( int payloadOffset = 0; int payloadLength = 0; int option = payload[offset++]; + bool serverSupportsEncryption = false; + bool serverSupportsCTAIP = false; while (option != (byte)PreLoginOptions.LASTOPT) { @@ -1415,29 +1417,33 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( break; case (int)PreLoginOptions.ENCRYPT: + if (tlsFirst) + { + // Can skip/ignore this option if we are doing TDS 8. + offset += 4; + break; + } + payloadOffset = payload[offset++] << 8 | payload[offset++]; payloadLength = payload[offset++] << 8 | payload[offset++]; EncryptionOptions serverOption = (EncryptionOptions)payload[payloadOffset]; /* internal enum EncryptionOptions { - OFF, - ON, - NOT_SUP, - REQ, - LOGIN - } */ + OFF, + ON, + NOT_SUP, + REQ, + LOGIN, + OPTIONS_MASK = 0x3f, + CTAIP = 0x40, + CLIENT_CERT = 0x80, + } */ + + // Any response other than NOT_SUP means the server supports encryption. + serverSupportsEncryption = (serverOption & EncryptionOptions.OPTIONS_MASK) != EncryptionOptions.NOT_SUP; + switch (_encryptionOption & EncryptionOptions.OPTIONS_MASK) { - case (EncryptionOptions.ON): - if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.NOT_SUP) - { - _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); - _physicalStateObj.Dispose(); - ThrowExceptionAndWarning(_physicalStateObj); - } - - break; - case (EncryptionOptions.OFF): if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.OFF) { @@ -1453,8 +1459,9 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( break; case (EncryptionOptions.NOT_SUP): - if (!tlsFirst && (serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.REQ) + if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.REQ) { + // Server requires encryption, but client does not support it. _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); ThrowExceptionAndWarning(_physicalStateObj); @@ -1463,37 +1470,20 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( break; default: - Debug.Fail("Invalid client encryption option detected"); + // Any other client option needs encryption + if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.NOT_SUP) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + break; } // Check if the server will accept CTAIP. // - if ((_encryptionOption & EncryptionOptions.CTAIP) != 0 && - (serverOption & EncryptionOptions.CTAIP) == 0) - { - _physicalStateObj.AddError(new SqlError(TdsEnums.CTAIP_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.CTAIPNotSupportedByServer(), "", 0)); - _physicalStateObj.Dispose(); - ThrowExceptionAndWarning(_physicalStateObj); - } - - if ((_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.ON || - (_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.LOGIN) - { - - if (serverCallback != null) - { - trustServerCert = true; - } - - // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert); - - UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) - | (is2005OrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); - - EnableSsl(info, encrypt, integratedSecurity, serverCertificateFilename, serverCallback, clientCallback); - } + serverSupportsCTAIP = (serverOption & EncryptionOptions.CTAIP) != 0; break; @@ -1576,6 +1566,37 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake( } } + if ((_encryptionOption & EncryptionOptions.CTAIP) != 0 && !serverSupportsCTAIP) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.CTAIP_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.CTAIPNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + + if ((_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.ON || + (_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.LOGIN) + { + if (!serverSupportsEncryption) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + + if (serverCallback != null) + { + trustServerCert = true; + } + + // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert); + + uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) + | (is2005OrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); + + EnableSsl(info, encrypt, integratedSecurity, serverCertificateFilename, serverCallback, clientCallback); + } + return PreLoginHandshakeStatus.Successful; } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index e61bdfe4ac..2ebf172786 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -35,6 +35,25 @@ public void IntegratedAuthConnectionTest() connection.Open(); } + /// + /// Runs a test where TDS Server doesn't send encryption info during pre-login response. + /// The driver is expected to fail when that happens, and terminate the connection during pre-login phase + /// when client enables encryption using Encrypt=true or uses default encryption setting. + /// + [Fact] + public async Task PreLoginEncryptionExcludedTest() + { + using TestTdsServer server = TestTdsServer.StartTestServer(false, false, 5, excludeEncryption: true); + SqlConnectionStringBuilder builder = new(server.ConnectionString) + { + IntegratedSecurity = true + }; + + using SqlConnection connection = new(builder.ConnectionString); + Exception ex = await Assert.ThrowsAsync(async () => await connection.OpenAsync()); + Assert.Contains("The instance of SQL Server you attempted to connect to does not support encryption.", ex.Message, StringComparison.OrdinalIgnoreCase); + } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] [InlineData(40613)] [InlineData(42108)] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs index 51e2330bf0..1ead74f58d 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs @@ -25,7 +25,7 @@ public TestTdsServer(QueryEngine engine, TDSServerArguments args) : base(args) Engine = engine; } - public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, [CallerMemberName] string methodName = "") + public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, bool excludeEncryption = false, [CallerMemberName] string methodName = "") { TDSServerArguments args = new TDSServerArguments() { @@ -36,6 +36,10 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool { args.FedAuthRequiredPreLoginOption = SqlServer.TDS.PreLogin.TdsPreLoginFedAuthRequiredOption.FedAuthRequired; } + if (excludeEncryption) + { + args.Encryption = SqlServer.TDS.PreLogin.TDSPreLoginTokenEncryptionType.None; + } TestTdsServer server = engine == null ? new TestTdsServer(args) : new TestTdsServer(engine, args); server._endpoint = new TDSServerEndPoint(server) { ServerEndPoint = new IPEndPoint(IPAddress.Any, 0) }; @@ -45,14 +49,17 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool server._endpoint.Start(); int port = server._endpoint.ServerEndPoint.Port; - server._connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = SqlConnectionEncryptOption.Optional }; + server._connectionStringBuilder = excludeEncryption + // Allow encryption to be set when encryption is to be excluded from pre-login response. + ? new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = SqlConnectionEncryptOption.Mandatory } + : new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = SqlConnectionEncryptOption.Optional }; server.ConnectionString = server._connectionStringBuilder.ConnectionString; return server; } - public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, [CallerMemberName] string methodName = "") + public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, bool excludeEncryption = false, [CallerMemberName] string methodName = "") { - return StartServerWithQueryEngine(null, enableFedAuth, enableLog, connectionTimeout, methodName); + return StartServerWithQueryEngine(null, enableFedAuth, enableLog, connectionTimeout, excludeEncryption, methodName); } public void Dispose() => _endpoint?.Stop(); diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerParser.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerParser.cs index a637e6bcc4..019fefd907 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerParser.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerParser.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using Microsoft.SqlServer.TDS.PreLogin; namespace Microsoft.SqlServer.TDS.EndPoint { @@ -68,8 +69,19 @@ public void Run() { case TDSMessageType.PreLogin: { + if (Session.Encryption == TDSEncryptionType.None) + { + (MessageBeingReceived[0] as TDSPreLoginToken).Encryption = TDSPreLoginTokenEncryptionType.None; + } + // Call into the subscriber to process the packet responseMessages = Server.OnPreLoginRequest(Session, MessageBeingReceived); + + if (Session.Encryption == TDSEncryptionType.None) + { + DisableTransportEncryption(); + } + break; } case TDSMessageType.TDS7Login: diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginToken.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginToken.cs index 7a5288f1c8..f5ca465fc0 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginToken.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginToken.cs @@ -331,7 +331,10 @@ public override void Deflate(Stream destination) options.Add(new TDSPreLoginTokenOption(TDSPreLoginTokenOptionType.NonceOption, (ushort)Nonce.Length)); } - options.Add(new TDSPreLoginTokenOption(TDSPreLoginTokenOptionType.Encryption, 1)); + if (Encryption != TDSPreLoginTokenEncryptionType.None) + { + options.Add(new TDSPreLoginTokenOption(TDSPreLoginTokenOptionType.Encryption, 1)); + } options.Add(new TDSPreLoginTokenOption(TDSPreLoginTokenOptionType.Terminator, 0)); // Calculate the total size of the token metadata diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginTokenEncryptionType.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginTokenEncryptionType.cs index fadb814579..82e69382e5 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginTokenEncryptionType.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginTokenEncryptionType.cs @@ -12,6 +12,7 @@ public enum TDSPreLoginTokenEncryptionType : byte Off = 0x00, On = 0x01, NotSupported = 0x02, - Required = 0x03 + Required = 0x03, + None = 0x10 } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSEncryptionType.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSEncryptionType.cs index 348605b894..4716273d73 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSEncryptionType.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSEncryptionType.cs @@ -22,6 +22,11 @@ public enum TDSEncryptionType /// /// Encryption of the entire session /// - Full + Full, + + /// + /// Excludes encryption option in Pre-Login response + /// + None } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSUtilities.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSUtilities.cs index a4bf123d6a..d8a0c292c5 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSUtilities.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSUtilities.cs @@ -261,11 +261,19 @@ public static TDSPreLoginTokenEncryptionType GetEncryptionResponse(TDSPreLoginTo { return TDSPreLoginTokenEncryptionType.On; } + else if (server == TDSPreLoginTokenEncryptionType.None) + { + return TDSPreLoginTokenEncryptionType.None; + } else { throw new ArgumentException("Server is configured to not support encryption", "server"); } } + else if (client == TDSPreLoginTokenEncryptionType.None) + { + return TDSPreLoginTokenEncryptionType.None; + } // This case is not documented so pick a default return TDSPreLoginTokenEncryptionType.Off; @@ -313,6 +321,10 @@ public static TDSEncryptionType ResolveEncryption(TDSPreLoginTokenEncryptionType return TDSEncryptionType.LoginOnly; } } + else if (client == TDSPreLoginTokenEncryptionType.None) + { + return TDSEncryptionType.None; + } // Full encryption is required return TDSEncryptionType.Full; From 6256edb83dcb3931a9694be9eef380a9fb7c7132 Mon Sep 17 00:00:00 2001 From: David Engel Date: Thu, 19 Oct 2023 17:24:28 +0000 Subject: [PATCH 17/76] Merged PR 4047: [5.1.3] --- .../VirtualSecureModeEnclaveProviderBase.cs | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index d612411aa8..e536017afb 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -161,8 +161,8 @@ private void VerifyAttestationInfo(string attestationUrl, HealthReport healthRep 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) + if (!VerifyHealthReportAgainstRootCertificate(signingCerts, healthReport.Certificate, out X509ChainStatusFlags chainStatus) || + 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 @@ -223,11 +223,20 @@ 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) + /// + /// 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). + /// + /// If the method returns false, the value of chainStatus doesn't matter. The chain could not be validated. + /// + /// + /// + /// + /// A that indicates if the certificate was able to be verified. + private bool VerifyHealthReportAgainstRootCertificate(X509Certificate2Collection signingCerts, X509Certificate2 healthReportCert, out X509ChainStatusFlags chainStatus) { var chain = new X509Chain(); + chainStatus = X509ChainStatusFlags.NoError; foreach (var cert in signingCerts) { @@ -249,9 +258,14 @@ private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certif } else { - return status.Status; + chainStatus = status.Status; + return true; } } + // The only ways past or out of the loop are: + // 1. untrustedRoot is true, in which case we want to continue to below + // 2. chainStatus is set to the first status in the chain and we return true + // 3. the ChainStatus is empty // 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 @@ -268,17 +282,21 @@ private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certif { if (element.Certificate.Thumbprint == cert.Thumbprint) { - return X509ChainStatusFlags.NoError; + return true; } } } // in the case where we didn't find matching thumbprint - return X509ChainStatusFlags.UntrustedRoot; + chainStatus = X509ChainStatusFlags.UntrustedRoot; + return true; } + + // There was an unknown failure and X509Chain.Build() returned an empty ChainStatus. + return false; } - return X509ChainStatusFlags.NoError; + return true; } // Verifies the enclave report signature using the health report. From b77f09ea65cdf01e3b4c8deda9774980163ae951 Mon Sep 17 00:00:00 2001 From: David Engel Date: Fri, 3 Nov 2023 16:24:09 +0000 Subject: [PATCH 18/76] Merged PR 4108: [5.1.4] Update dependency versions Update dependency versions for Azure Identity, Azure Core, MSAL, and DiagnosticSource. Addresses Azure Identity vulnerability. --- tools/props/Versions.props | 8 ++++---- tools/specs/Microsoft.Data.SqlClient.nuspec | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index e3a0355956..541ff51a9d 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -27,8 +27,8 @@ - 1.7.0 - 4.47.2 + 1.10.3 + 4.56.0 6.24.0 6.24.0 4.5.1 @@ -41,7 +41,7 @@ 5.1.1 6.0.1 1.0.0 - 6.0.0 + 6.0.1 6.0.1 6.0.0 5.0.0 @@ -55,7 +55,7 @@ - [1.25.0,2.0.0) + [1.35.0,2.0.0) [4.4.0,5.0.0) 6.0.1 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 0f9a5158d1..e77524bd34 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -29,8 +29,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -40,13 +40,13 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + - + @@ -55,8 +55,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -72,8 +72,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + From 5cc6ca8113eeb20dc33d79743b046300dc9e5412 Mon Sep 17 00:00:00 2001 From: David Engel Date: Fri, 3 Nov 2023 20:31:44 +0000 Subject: [PATCH 19/76] Merged PR 4120: [5.1.4] Backport #2161 - Fix deadlock in transaction against .NET 7 Backport [GH PR #2161](https://github.com/dotnet/SqlClient/pull/1242) - Fix deadlock in transaction against .NET 7 Related work items: #1242 --- .../Data/SqlClient/SqlDelegatedTransaction.cs | 86 ++++++++++--------- .../Data/SqlClient/SqlDelegatedTransaction.cs | 1 - .../ManualTests/DataCommon/DataTestUtility.cs | 11 +++ .../TransactionEnlistmentTest.cs | 11 +-- 4 files changed, 58 insertions(+), 51 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 680b34079d..848e69aa06 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 @@ -348,21 +348,23 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) #endif try { - Exception commitException = null; - - 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) { - // 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) + lock (connection) { _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; - - enlistment.Aborted(SQL.ConnectionDoomed()); } - else + + enlistment.Aborted(SQL.ConnectionDoomed()); + } + else + { + Exception commitException; + lock (connection) { try { @@ -370,9 +372,10 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) ValidateActiveOnConnection(connection); _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. - _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event + _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); + commitException = null; } catch (SqlException e) { @@ -391,42 +394,41 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } - if (commitException != null) + } + if (commitException != null) + { + // connection.ExecuteTransaction failed with exception + if (_internalTransaction.IsCommitted) { - // 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. + // 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); } - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + // 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. } - } - if (commitException == null) - { - // connection.ExecuteTransaction succeeded - enlistment.Committed(); + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + if (commitException == null) + { + // connection.ExecuteTransaction succeeded + enlistment.Committed(); + } } } catch (System.OutOfMemoryException e) 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 bf7f86c3a7..ebc234d480 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 @@ -387,7 +387,6 @@ public void SinglePhaseCommit(SysTx.SinglePhaseEnlistment enlistment) Debug.Assert(null != enlistment, "null enlistment?"); SqlInternalConnection connection = GetValidConnection(); - if (null != connection) { SqlConnection usersConnection = connection.Connection; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 37e83cbd98..1d4c0adf6b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -441,6 +441,17 @@ public static string GetUniqueNameForSqlServer(string prefix, bool withBracket = return name; } + public static bool IsSupportingDistributedTransactions() + { +#if NET7_0_OR_GREATER + return OperatingSystem.IsWindows() && System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture != System.Runtime.InteropServices.Architecture.X86 && IsNotAzureServer(); +#elif NETFRAMEWORK + return IsNotAzureServer(); +#else + return false; +#endif + } + public static void DropTable(SqlConnection sqlConnection, string tableName) { ResurrectConnection(sqlConnection); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs index 34061606f4..ade9f41844 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs @@ -47,11 +47,10 @@ public static void TestManualEnlistment_Enlist_TxScopeComplete() RunTestSet(TestCase_ManualEnlistment_Enlist_TxScopeComplete); } - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp)] - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSupportingDistributedTransactions))] public static void TestEnlistmentPrepare_TxScopeComplete() { - try + Assert.Throws(() => { using TransactionScope txScope = new(TransactionScopeOption.RequiresNew, new TransactionOptions() { @@ -64,11 +63,7 @@ public static void TestEnlistmentPrepare_TxScopeComplete() System.Transactions.Transaction.Current.EnlistDurable(EnlistmentForPrepare.s_id, new EnlistmentForPrepare(), EnlistmentOptions.None); txScope.Complete(); Assert.False(true, "Expected exception not thrown."); - } - catch (Exception e) - { - Assert.True(e is TransactionAbortedException); - } + }); } private static void TestCase_AutoEnlistment_TxScopeComplete() From b92637e0fe65dc0fe0dd4a460ee802bfb4ad5a06 Mon Sep 17 00:00:00 2001 From: Javad Date: Fri, 26 Jan 2024 09:53:42 -0800 Subject: [PATCH 20/76] [5.1.5] CVE | Version bump Microsoft.IdentityModel.JsonWebTokens to 6.35.0 (#2290) (#2320) --- tools/props/Versions.props | 6 +++--- tools/specs/Microsoft.Data.SqlClient.nuspec | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 541ff51a9d..1e8cf6a3b0 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -29,8 +29,8 @@ 1.10.3 4.56.0 - 6.24.0 - 6.24.0 + 6.35.0 + 6.35.0 4.5.1 6.0.0 1.1.0 @@ -67,7 +67,7 @@ 13.0.1 4.3.0 6.0.1 - 6.24.0 + 6.35.0 2.4.2 2.4.5 7.0.0-beta.22316.1 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index e77524bd34..f7e364e405 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -31,8 +31,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -42,8 +42,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -57,8 +57,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -74,8 +74,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + From f8520c7b9b65a9aa90c8bce756e81ad22c4452da Mon Sep 17 00:00:00 2001 From: Javad Date: Fri, 26 Jan 2024 12:34:28 -0800 Subject: [PATCH 21/76] Fix | Invalid transaction exception against the connections and distributed transactions (#2301) (#2321) --- .../Data/ProviderBase/DbConnectionInternal.cs | 14 ++++++++++---- .../Data/SqlClient/SqlDelegatedTransaction.cs | 6 +++--- .../Data/ProviderBase/DbConnectionInternal.cs | 17 ++++++++++------- .../Data/SqlClient/SqlDelegatedTransaction.cs | 6 +++--- 4 files changed, 26 insertions(+), 17 deletions(-) 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 8fc8df24e0..9f4d5d2e08 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 @@ -16,6 +16,7 @@ internal abstract partial class DbConnectionInternal { private static int _objectTypeCount; internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + private TransactionCompletedEventHandler _transactionCompletedEventHandler = null; private bool _isInStasis; @@ -437,15 +438,19 @@ internal void DetachTransaction(Transaction transaction, bool isExplicitlyReleas // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should // be the exception, not the rule. - lock (this) + // locking on anything other than the transaction object would lead to a thread deadlock with sys.Transaction.TransactionCompleted event. + lock (transaction) { // Detach if detach-on-end behavior, or if outer connection was closed - DbConnection owner = (DbConnection)Owner; - if (isExplicitlyReleasing || UnbindOnTransactionCompletion || null == owner) + DbConnection owner = Owner; + if (isExplicitlyReleasing || UnbindOnTransactionCompletion || owner is null) { Transaction currentEnlistedTransaction = _enlistedTransaction; if (currentEnlistedTransaction != null && transaction.Equals(currentEnlistedTransaction)) { + // We need to remove the transaction completed event handler to cease listening for the transaction to end. + currentEnlistedTransaction.TransactionCompleted -= _transactionCompletedEventHandler; + EnlistedTransaction = null; if (IsTxRootWaitingForTxEnd) @@ -479,7 +484,8 @@ void TransactionCompletedEvent(object sender, TransactionEventArgs e) private void TransactionOutcomeEnlist(Transaction transaction) { - transaction.TransactionCompleted += new TransactionCompletedEventHandler(TransactionCompletedEvent); + _transactionCompletedEventHandler ??= new TransactionCompletedEventHandler(TransactionCompletedEvent); + transaction.TransactionCompleted += _transactionCompletedEventHandler; } internal void SetInStasis() 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 848e69aa06..3ed756eef0 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 @@ -159,17 +159,17 @@ public byte[] Promote() ValidateActiveOnConnection(connection); connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); - returnValue = _connection.PromotedDTCToken; + returnValue = connection.PromotedDTCToken; // For Global Transactions, we need to set the Transaction Id since we use a Non-MSDTC Promoter type. - if (_connection.IsGlobalTransaction) + if (connection.IsGlobalTransaction) { if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) { throw SQL.UnsupportedSysTxForGlobalTransactions(); } - if (!_connection.IsGlobalTransactionsEnabledForServer) + if (!connection.IsGlobalTransactionsEnabledForServer) { throw SQL.GlobalTransactionsNotEnabled(); } 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 a79eb68411..06b1e04712 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 @@ -18,11 +18,10 @@ namespace Microsoft.Data.ProviderBase using SysTx = System.Transactions; internal abstract class DbConnectionInternal - { // V1.1.3300 - - + { private static int _objectTypeCount; internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + private SysTx.TransactionCompletedEventHandler _transactionCompletedEventHandler = null; internal static readonly StateChangeEventArgs StateChangeClosed = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed); internal static readonly StateChangeEventArgs StateChangeOpen = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open); @@ -900,15 +899,18 @@ internal void DetachTransaction(SysTx.Transaction transaction, bool isExplicitly // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should // be the exception, not the rule. - lock (this) + // locking on anything other than the transaction object would lead to a thread deadlock with sys.Transaction.TransactionCompleted event. + lock (transaction) { // Detach if detach-on-end behavior, or if outer connection was closed - DbConnection owner = (DbConnection)Owner; - if (isExplicitlyReleasing || UnbindOnTransactionCompletion || null == owner) + DbConnection owner = Owner; + if (isExplicitlyReleasing || UnbindOnTransactionCompletion || owner is null) { SysTx.Transaction currentEnlistedTransaction = _enlistedTransaction; if (currentEnlistedTransaction != null && transaction.Equals(currentEnlistedTransaction)) { + // We need to remove the transaction completed event handler to cease listening for the transaction to end. + currentEnlistedTransaction.TransactionCompleted -= _transactionCompletedEventHandler; EnlistedTransaction = null; @@ -947,7 +949,8 @@ void TransactionCompletedEvent(object sender, SysTx.TransactionEventArgs e) [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] private void TransactionOutcomeEnlist(SysTx.Transaction transaction) { - transaction.TransactionCompleted += new SysTx.TransactionCompletedEventHandler(TransactionCompletedEvent); + _transactionCompletedEventHandler ??= new SysTx.TransactionCompletedEventHandler(TransactionCompletedEvent); + transaction.TransactionCompleted += _transactionCompletedEventHandler; } internal void SetInStasis() 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 ebc234d480..e11ec36e79 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 @@ -192,17 +192,17 @@ public Byte[] Promote() ValidateActiveOnConnection(connection); connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, IsolationLevel.Unspecified, _internalTransaction, true); - returnValue = _connection.PromotedDTCToken; + returnValue = connection.PromotedDTCToken; // For Global Transactions, we need to set the Transaction Id since we use a Non-MSDTC Promoter type. - if (_connection.IsGlobalTransaction) + if (connection.IsGlobalTransaction) { if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) { throw SQL.UnsupportedSysTxForGlobalTransactions(); } - if (!_connection.IsGlobalTransactionsEnabledForServer) + if (!connection.IsGlobalTransactionsEnabledForServer) { throw SQL.GlobalTransactionsNotEnabled(); } From 759dc6924650d9f933876564c7d4b69739bbbcf3 Mon Sep 17 00:00:00 2001 From: Javad Date: Fri, 26 Jan 2024 16:08:32 -0800 Subject: [PATCH 22/76] Fix | Minor fixes to support different test environments (#2045) (#2325) --- .../src/Microsoft/Data/SqlClient/SNI/SSRP.cs | 36 ++++++++++++- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 -- .../netcore/src/Resources/Strings.Designer.cs | 9 ---- .../netcore/src/Resources/Strings.resx | 3 -- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 50 +++++++++---------- .../ManualTests/AlwaysEncrypted/BulkCopyAE.cs | 2 +- .../AlwaysEncrypted/BulkCopyAEErrorMessage.cs | 2 +- .../AlwaysEncrypted/End2EndSmokeTests.cs | 4 +- .../AlwaysEncrypted/SqlBulkCopyTruncation.cs | 28 +++++------ .../AlwaysEncrypted/SqlNullValues.cs | 2 +- .../ManualTests/DataCommon/DataTestUtility.cs | 29 +++++++++-- .../SQL/InstanceNameTest/InstanceNameTest.cs | 6 ++- .../IntegratedAuthenticationTest.cs | 8 ++- 13 files changed, 114 insertions(+), 69 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs index e51175059a..49d68b0120 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs @@ -48,8 +48,14 @@ internal static int GetPortByInstanceName(string browserHostName, string instanc } catch (SocketException se) { + // A SocketException is possible for an instance name that doesn't exist. + // If there are multiple IP addresses and one of them fails with a SocketException but + // others simply don't respond because the instance name is invalid, we want to return + // the same error as if the response was empty. The higher error suits all scenarios. + // But log it, just in case there is a different, underlying issue that support needs + // to troubleshoot. SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.ERR, "SocketException Message = {0}", args0: se?.Message); - throw new Exception(SQLMessage.SqlServerBrowserNotAccessible(), se); + throw; } const byte SvrResp = 0x05; @@ -321,9 +327,37 @@ private static SsrpResult SendUDPRequest(IPEndPoint endPoint, byte[] requestPack } } } + catch (AggregateException ae) + { + if (ae.InnerExceptions.Count > 0) + { + // Log all errors + foreach (Exception e in ae.InnerExceptions) + { + // Favor SocketException for returned error + if (e is SocketException) + { + result.Error = e; + } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, + "SendUDPRequest ({0}) resulted in exception: {1}", args0: endPoint.ToString(), args1: e.Message); + } + + // Return first error if we didn't find a SocketException + result.Error = result.Error == null ? ae.InnerExceptions[0] : result.Error; + } + else + { + result.Error = ae; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, + "SendUDPRequest ({0}) resulted in exception: {1}", args0: endPoint.ToString(), args1: ae.Message); + } + } catch (Exception e) { result.Error = e; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, + "SendUDPRequest ({0}) resulted in exception: {1}", args0: endPoint.ToString(), args1: e.Message); } return result; 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 4aebe4b518..0947d6d849 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 @@ -1984,10 +1984,6 @@ internal static string SSPIGenerateError() { return StringsHelper.GetString(Strings.SQL_SSPIGenerateError); } - internal static string SqlServerBrowserNotAccessible() - { - return StringsHelper.GetString(Strings.SQL_SqlServerBrowserNotAccessible); - } internal static string KerberosTicketMissingError() { return StringsHelper.GetString(Strings.SQL_KerberosTicketMissingError); 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 21e213b262..bba9987634 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -3201,15 +3201,6 @@ internal static string SQL_SqlCommandCommandText { } } - /// - /// Looks up a localized string similar to Cannot connect to SQL Server Browser. Ensure SQL Server Browser has been started.. - /// - internal static string SQL_SqlServerBrowserNotAccessible { - get { - return ResourceManager.GetString("SQL_SqlServerBrowserNotAccessible", resourceCulture); - } - } - /// /// Looks up a localized string similar to Failed to generate SSPI context.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index b9bbac1567..28999773e0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -537,9 +537,6 @@ Cannot authenticate using Kerberos. Ensure Kerberos has been initialized on the client with 'kinit' and a Service Principal Name has been registered for the SQL Server to allow Kerberos authentication. - - Cannot connect to SQL Server Browser. Ensure SQL Server Browser has been started. - Invalid SSPI packet size. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index bd6060c179..62db90fce0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -57,7 +57,7 @@ public ApiShould(PlatformSpecificTestContext context) DummyKeyStoreProvider.Name, _lastTenBytesCek); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithBooleanVariable))] public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connection, bool isCommitted) { @@ -94,7 +94,7 @@ public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connect } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestSqlTransactionRollbackToSavePoint(string connection) { @@ -140,7 +140,7 @@ public void TestSqlTransactionRollbackToSavePoint(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void SqlParameterProperties(string connection) { @@ -351,7 +351,7 @@ public void SqlParameterProperties(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestSqlDataAdapterFillDataTable(string connection) { @@ -424,7 +424,7 @@ public void TestSqlDataAdapterFillDataTable(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithSchemaType))] public void TestSqlDataAdapterFillSchema(string connection, SchemaType schemaType) { @@ -471,7 +471,7 @@ private void ValidateSchema(DataColumnCollection dataColumns) Assert.Equal(typeof(string), dataColumns[2].DataType); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithBooleanVariable))] public void TestExecuteNonQuery(string connection, bool isAsync) { @@ -539,7 +539,7 @@ public void TestExecuteNonQuery(string connection, bool isAsync) }); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithBooleanVariable))] public void TestExecuteScalar(string connection, bool isAsync) { @@ -591,7 +591,7 @@ public void TestExecuteScalar(string connection, bool isAsync) }); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithIntegers))] public void TestSqlDataAdapterBatchUpdate(string connection, int numberofRows) { @@ -636,7 +636,7 @@ public void TestSqlDataAdapterBatchUpdate(string connection, int numberofRows) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestExecuteReader(string connection) { @@ -690,7 +690,7 @@ public void TestExecuteReader(string connection) }); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public async void TestExecuteReaderAsyncWithLargeQuery(string connectionString) { @@ -743,7 +743,7 @@ public async void TestExecuteReaderAsyncWithLargeQuery(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet1))] public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehavior commandBehavior) { @@ -868,7 +868,7 @@ public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehav }); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestEnclaveStoredProceduresWithAndWithoutParameters(string connectionString) { @@ -915,7 +915,7 @@ public void TestEnclaveStoredProceduresWithAndWithoutParameters(string connectio } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestPrepareWithExecuteNonQuery(string connection) { @@ -964,7 +964,7 @@ public void TestPrepareWithExecuteNonQuery(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestAsyncWriteDelayWithExecuteNonQueryAsync(string connection) { @@ -1018,7 +1018,7 @@ public void TestAsyncWriteDelayWithExecuteNonQueryAsync(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestAsyncWriteDelayWithExecuteReaderAsync(string connection) { @@ -1085,7 +1085,7 @@ public void TestAsyncWriteDelayWithExecuteReaderAsync(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestPrepareWithExecuteNonQueryAsync(string connection) { @@ -1140,7 +1140,7 @@ public void TestPrepareWithExecuteNonQueryAsync(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet2))] public void TestPrepareWithExecuteReaderAsync(string connection, CommandBehavior commandBehavior) { @@ -1202,7 +1202,7 @@ public void TestPrepareWithExecuteReaderAsync(string connection, CommandBehavior } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestSqlDataReaderAPIs(string connection) { @@ -1399,7 +1399,7 @@ public void TestSqlDataReaderAPIs(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestSqlDataReaderAPIsWithSequentialAccess(string connection) { @@ -1827,7 +1827,7 @@ public void TestSqlDataReaderAPIsWithSequentialAccess(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet2))] public void TestSqlCommandSequentialAccessCodePaths(string connection, CommandBehavior value) { @@ -1871,7 +1871,7 @@ public void TestSqlCommandSequentialAccessCodePaths(string connection, CommandBe } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestExecuteXmlReader(string connection) { @@ -2126,7 +2126,7 @@ public void TestSqlCommandCancel(string connection, string value, int number) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProviderWithCancellationTime))] public void TestSqlCommandCancellationToken(string connection, int initalValue, int cancellationTime) { @@ -2194,7 +2194,7 @@ public void TestNoneAttestationProtocolWithSGXEnclave() } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestConnectionCustomKeyStoreProviderDuringAeQuery(string connectionString) { @@ -2249,7 +2249,7 @@ public void TestConnectionCustomKeyStoreProviderDuringAeQuery(string connectionS } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.IsAKVSetupAvailable))] [ClassData(typeof(AEConnectionStringProvider))] public void TestCommandCustomKeyStoreProviderDuringAeQuery(string connectionString) { @@ -2300,7 +2300,7 @@ 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))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestSystemProvidersHavePrecedenceOverInstanceLevelProviders(string connectionString) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs index 6a58935cff..20cd452286 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs @@ -24,7 +24,7 @@ public BulkCopyAE(PlatformSpecificTestContext context) tableName = fixture.BulkCopyAETestTable.Name; } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TestBulkCopyString(string connectionString) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAEErrorMessage.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAEErrorMessage.cs index cf2da1c4b6..9d6080b24f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAEErrorMessage.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAEErrorMessage.cs @@ -25,7 +25,7 @@ public BulkCopyAEErrorMessage(PlatformSpecificTestContext context) _columnName = "c1"; } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void TextToIntErrorMessageTest(string connectionString) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs index 4862a41d03..ea4b0171eb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs @@ -24,7 +24,7 @@ public End2EndSmokeTests(PlatformSpecificTestContext context) } // tests - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsData))] public void TestSelectOnEncryptedNonEncryptedColumns(string connString, string selectQuery, int totalColumnsInSelect, string[] types) { @@ -58,7 +58,7 @@ public void TestSelectOnEncryptedNonEncryptedColumns(string connString, string s } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsWithEncryptedParametersData))] public void TestSelectOnEncryptedNonEncryptedColumnsWithEncryptedParameters(string connString, bool sync, diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlBulkCopyTruncation.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlBulkCopyTruncation.cs index 32dd86cd94..e5c4429752 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlBulkCopyTruncation.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlBulkCopyTruncation.cs @@ -23,7 +23,7 @@ public SqlBulkCopyTruncation(PlatformSpecificTestContext context) tableNames = _fixture.sqlBulkTruncationTableNames; } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopyTestsInt(string connectionString) { @@ -40,7 +40,7 @@ public void BulkCopyTestsInt(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void DirectInsertTest1(string connectionString) { @@ -80,7 +80,7 @@ public void DirectInsertTest1(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void DirectInsertTest2(string connectionString) { @@ -127,7 +127,7 @@ public void DirectInsertTest2(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void DirectInsertTest3(string connectionString) { @@ -172,7 +172,7 @@ public void DirectInsertTest3(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void DirectInsertTest4(string connectionString) { @@ -209,7 +209,7 @@ public void DirectInsertTest4(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopyDatetime2Tests(string connectionString) { @@ -258,7 +258,7 @@ public void BulkCopyDatetime2Tests(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopyDecimal(string connectionString) { @@ -275,7 +275,7 @@ public void BulkCopyDecimal(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopyVarchar(string connectionString) { @@ -304,7 +304,7 @@ public void BulkCopyVarchar(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopyVarcharMax(string connectionString) { @@ -333,7 +333,7 @@ public void BulkCopyVarcharMax(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopyNVarchar(string connectionString) { @@ -350,7 +350,7 @@ public void BulkCopyNVarchar(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopyNVarcharMax(string connectionString) { @@ -367,7 +367,7 @@ public void BulkCopyNVarcharMax(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopyBinaryMax(string connectionString) { @@ -407,7 +407,7 @@ public void BulkCopyBinaryMax(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopySmallBinary(string connectionString) { @@ -476,7 +476,7 @@ public void BulkCopySmallBinary(string connectionString) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(AEConnectionStringProvider))] public void BulkCopySmallChar(string connectionString) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs index 2f1a1d8ed6..ed3d9797dd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs @@ -56,7 +56,7 @@ public SqlNullValuesTests(PlatformSpecificTestContext context) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] [ClassData(typeof(NullValueTestsData))] public void NullValueTests(string connString, ConnStringColumnEncryptionSetting connStringSetting, SqlCommandColumnEncryptionSetting commandSetting, ReturnValueSetting nullReturnValue) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 1d4c0adf6b..4853806246 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -341,6 +341,16 @@ public static bool IsAKVSetupAvailable() return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(AKVClientId) && !string.IsNullOrEmpty(AKVClientSecret) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse(); } + public static bool IsTargetReadyForAeWithKeyStore() + { + return DataTestUtility.AreConnStringSetupForAE() +#if NET6_0_OR_GREATER + // AE tests on Windows will use the Cert Store. On non-Windows, they require AKV. + && (OperatingSystem.IsWindows() || DataTestUtility.IsAKVSetupAvailable()) +#endif + ; + } + public static bool IsUsingManagedSNI() => UseManagedSNIOnWindows; public static bool IsNotUsingManagedSNIOnWindows() => !UseManagedSNIOnWindows; @@ -946,15 +956,24 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData) /// Resolves the machine's fully qualified domain name if it is applicable. /// /// Returns FQDN if the client was domain joined otherwise the machine name. - public static string GetMachineFQDN() + public static string GetMachineFQDN(string hostname) { IPGlobalProperties machineInfo = IPGlobalProperties.GetIPGlobalProperties(); StringBuilder fqdn = new(); - fqdn.Append(machineInfo.HostName); - if (!string.IsNullOrEmpty(machineInfo.DomainName)) + if (hostname.Equals("localhost", StringComparison.OrdinalIgnoreCase) || + hostname.Equals(machineInfo.HostName, StringComparison.OrdinalIgnoreCase)) + { + fqdn.Append(machineInfo.HostName); + if (!string.IsNullOrEmpty(machineInfo.DomainName)) + { + fqdn.Append("."); + fqdn.Append(machineInfo.DomainName); + } + } + else { - fqdn.Append("."); - fqdn.Append(machineInfo.DomainName); + IPHostEntry host = Dns.GetHostEntry(hostname); + fqdn.Append(host.HostName); } return fqdn.ToString(); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs index 6edc9f00a6..1ae87217b6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs @@ -33,7 +33,10 @@ public static void ConnectToSQLWithInstanceNameTest() connection.Open(); connection.Close(); - if (builder.Encrypt != SqlConnectionEncryptOption.Strict) + // We can only connect via IP address if we aren't doing remote Kerberos or strict TLS + if (builder.Encrypt != SqlConnectionEncryptOption.Strict && + (!builder.IntegratedSecurity || hostname.Equals("localhost", StringComparison.OrdinalIgnoreCase) || + hostname.Equals(Environment.MachineName, StringComparison.OrdinalIgnoreCase))) { // Exercise the IP address-specific code in SSRP IPAddress[] addresses = Dns.GetHostAddresses(hostname); @@ -64,7 +67,6 @@ public static void ConnectManagedWithInstanceNameTest(bool useMultiSubnetFailove if (IsBrowserAlive(hostname) && IsValidInstance(hostname, instanceName)) { builder.DataSource = hostname + "\\" + instanceName; - using SqlConnection connection = new(builder.ConnectionString); connection.Open(); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs index 6cd19714ae..654d020742 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs @@ -46,7 +46,13 @@ public static void IntegratedAuthenticationTest_ServerSPN() { SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); builder.IntegratedSecurity = true; - builder.ServerSPN = $"MSSQLSvc/{DataTestUtility.GetMachineFQDN()}"; + Assert.True(DataTestUtility.ParseDataSource(builder.DataSource, out string hostname, out int port, out string instanceName)); + // Build the SPN for the server we are connecting to + builder.ServerSPN = $"MSSQLSvc/{DataTestUtility.GetMachineFQDN(hostname)}"; + if (!string.IsNullOrWhiteSpace(instanceName)) + { + builder.ServerSPN += ":" + instanceName; + } TryOpenConnectionWithIntegratedAuthentication(builder.ConnectionString); } From 01a589e54afe43c85c7607aa05f8605fa6f13bb3 Mon Sep 17 00:00:00 2001 From: Javad Date: Fri, 26 Jan 2024 17:39:03 -0800 Subject: [PATCH 23/76] [5.1.5] Fix | Enable reading AE date as DateOnly (#2275) (#2324) --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 4 +- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 4 + .../AlwaysEncrypted/DateOnlyReadTests.cs | 93 +++++++++++++++++++ .../TestFixtures/DatabaseHelper.cs | 29 ++++++ .../TestFixtures/SQLSetupStrategy.cs | 7 +- .../TestFixtures/Setup/DateOnlyTestTable.cs | 42 +++++++++ .../ManualTests/DataCommon/DataTestUtility.cs | 1 - ....Data.SqlClient.ManualTesting.Tests.csproj | 4 + 8 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 965803f993..d72fbbf4c9 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 @@ -2844,11 +2844,11 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met return (T)(object)data.DateTime; } #if NET6_0_OR_GREATER - else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) + else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005) { return (T)(object)data.DateOnly; } - else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) + else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005) { return (T)(object)data.TimeOnly; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 62db90fce0..8e7458f02e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -3169,6 +3169,10 @@ public Customer(int id, string firstName, string lastName) public string LastName { get; set; } } +#if NET6_0_OR_GREATER + public record CustomerDateOnly(int Id, string FirstName, string LastName, DateOnly DateOfBirth, TimeOnly TimeOfDay); +#endif + internal class TestAsyncCallBackStateObject { /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs new file mode 100644 index 0000000000..a211fe6343 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/DateOnlyReadTests.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted +{ + public sealed class DateOnlyReadTests : IClassFixture, IDisposable + { + private SQLSetupStrategy fixture; + + private readonly string tableName; + + public DateOnlyReadTests(PlatformSpecificTestContext context) + { + fixture = context.Fixture; + tableName = fixture.DateOnlyTestTable.Name; + } + + // tests + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTargetReadyForAeWithKeyStore))] + [ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsDataDateOnly))] + public void TestSelectOnEncryptedNonEncryptedColumns(string connString, string selectQuery, int totalColumnsInSelect, string[] types) + { + Assert.False(string.IsNullOrWhiteSpace(selectQuery), "FAILED: select query should not be null or empty."); + Assert.True(totalColumnsInSelect <= 3, "FAILED: totalColumnsInSelect should <= 3."); + + using (SqlConnection sqlConn = new SqlConnection(connString)) + { + sqlConn.Open(); + + Table.DeleteData(tableName, sqlConn); + + // insert 1 row data + CustomerDateOnly customer = new CustomerDateOnly( + 45, + "Microsoft", + "Corporation", + new DateOnly(2001, 1, 31), + new TimeOnly(18, 36, 45)); + + DatabaseHelper.InsertCustomerDateOnlyData(sqlConn, null, tableName, customer); + + using (SqlCommand sqlCommand = new SqlCommand(string.Format(selectQuery, tableName), + sqlConn, null, SqlCommandColumnEncryptionSetting.Enabled)) + { + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) + { + Assert.True(sqlDataReader.HasRows, "FAILED: Select statement did not return any rows."); + + while (sqlDataReader.Read()) + { + DatabaseHelper.CompareResults(sqlDataReader, types, totalColumnsInSelect); + } + } + } + } + } + + + public void Dispose() + { + foreach (string connStrAE in DataTestUtility.AEConnStringsSetup) + { + using (SqlConnection sqlConnection = new SqlConnection(connStrAE)) + { + sqlConnection.Open(); + Table.DeleteData(fixture.DateOnlyTestTable.Name, sqlConnection); + } + } + } + } + + public class TestSelectOnEncryptedNonEncryptedColumnsDataDateOnly : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, @"select CustomerId, FirstName, LastName from [{0}] ", 3, new string[] { @"int", @"string", @"string" } }; + yield return new object[] { connStrAE, @"select CustomerId, FirstName from [{0}] ", 2, new string[] { @"int", @"string" } }; + yield return new object[] { connStrAE, @"select LastName from [{0}] ", 1, new string[] { @"string" }}; + yield return new object[] { connStrAE, @"select DateOfBirth, TimeOfDay from [{0}] ", 2, new string[] { @"DateOnly", "TimeOnly" } }; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index 9cea6b8239..86fa2c38c5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -31,6 +31,27 @@ internal static void InsertCustomerData(SqlConnection sqlConnection, SqlTransact sqlCommand.ExecuteNonQuery(); } +#if NET6_0_OR_GREATER + /// + /// Insert CustomerDateOnly record into table + /// + internal static void InsertCustomerDateOnlyData(SqlConnection sqlConnection, SqlTransaction transaction, string tableName, CustomerDateOnly customer) + { + using SqlCommand sqlCommand = new( + $"INSERT INTO [{tableName}] (CustomerId, FirstName, LastName, DateOfBirth, TimeOfDay) VALUES (@CustomerId, @FirstName, @LastName, @DateOfBirth, @TimeOfDay);", + connection: sqlConnection, + transaction: transaction, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled); + + sqlCommand.Parameters.AddWithValue(@"CustomerId", customer.Id); + sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName); + sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName); + sqlCommand.Parameters.AddWithValue(@"DateOfBirth", customer.DateOfBirth); + sqlCommand.Parameters.AddWithValue(@"TimeOfDay", customer.TimeOfDay); + sqlCommand.ExecuteNonQuery(); + } +#endif + /// /// Validates that the results are the ones expected. /// @@ -155,7 +176,15 @@ public static void CompareResults(SqlDataReader sqlDataReader, string[] paramete case "int": Assert.True(sqlDataReader.GetInt32(columnsRead) == 45, "FAILED: read int value does not match."); break; +#if NET6_0_OR_GREATER + case "DateOnly": + Assert.True(sqlDataReader.GetFieldValue(columnsRead) == new DateOnly(2001, 1, 31), "FAILED: read DateOnly value does not match."); + break; + case "TimeOnly": + Assert.True(sqlDataReader.GetFieldValue(columnsRead) == new TimeOnly(18, 36, 45), "FAILED: read TimeOnly value does not match."); + break; +#endif default: Assert.True(false, "FAILED: unexpected data type."); break; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs index 0cd8436cb4..4d3c635684 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -1,4 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. + +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information.using System; @@ -21,6 +22,7 @@ public class SQLSetupStrategy : IDisposable public Table BulkCopyAEErrorMessageTestTable { get; private set; } public Table BulkCopyAETestTable { get; private set; } public Table SqlParameterPropertiesTable { get; private set; } + public Table DateOnlyTestTable { get; private set; } public Table End2EndSmokeTable { get; private set; } public Table TrustedMasterKeyPathsTestTable { get; private set; } public Table SqlNullValuesTable { get; private set; } @@ -131,6 +133,9 @@ protected List CreateTables(IList columnEncryptionKe End2EndSmokeTable = new ApiTestTable(GenerateUniqueName("End2EndSmokeTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); tables.Add(End2EndSmokeTable); + DateOnlyTestTable = new DateOnlyTestTable(GenerateUniqueName("DateOnlyTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); + tables.Add(DateOnlyTestTable); + TrustedMasterKeyPathsTestTable = new ApiTestTable(GenerateUniqueName("TrustedMasterKeyPathsTestTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); tables.Add(TrustedMasterKeyPathsTestTable); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs new file mode 100644 index 0000000000..6cc9bb6f66 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/DateOnlyTestTable.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup +{ + public class DateOnlyTestTable : Table + { + private const string ColumnEncryptionAlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA_256"; + public ColumnEncryptionKey columnEncryptionKey1; + public ColumnEncryptionKey columnEncryptionKey2; + private bool useDeterministicEncryption; + + public DateOnlyTestTable(string tableName, ColumnEncryptionKey columnEncryptionKey1, ColumnEncryptionKey columnEncryptionKey2, bool useDeterministicEncryption = false) : base(tableName) + { + this.columnEncryptionKey1 = columnEncryptionKey1; + this.columnEncryptionKey2 = columnEncryptionKey2; + this.useDeterministicEncryption = useDeterministicEncryption; + } + + public override void Create(SqlConnection sqlConnection) + { + string encryptionType = useDeterministicEncryption ? "DETERMINISTIC" : DataTestUtility.EnclaveEnabled ? "RANDOMIZED" : "DETERMINISTIC"; + string sql = + $@"CREATE TABLE [dbo].[{Name}] + ( + [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [TimeOfDay] [time] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [DateOfBirth] [date] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}') + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 4853806246..c92425561c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -61,7 +61,6 @@ public static class DataTestUtility public static readonly bool IsDNSCachingSupportedTR = false; // this is for the tenant ring public static readonly string UserManagedIdentityClientId = null; - public static readonly string EnclaveAzureDatabaseConnString = null; public static bool ManagedIdentitySupported = true; public static string AADAccessToken = null; 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 5172452626..9f0cb5a11b 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 @@ -57,6 +57,7 @@ + @@ -70,6 +71,9 @@ + + + From d709ebb5f156737d745f79bcb96819b089f3aa91 Mon Sep 17 00:00:00 2001 From: Javad Date: Fri, 9 Feb 2024 09:21:03 -0800 Subject: [PATCH 24/76] Release notes 5.1.5 (#2327) --- CHANGELOG.md | 66 +++++++++++++++++++++++++++++++++++ release-notes/5.1/5.1.5.md | 68 +++++++++++++++++++++++++++++++++++++ release-notes/5.1/5.1.md | 5 +++ release-notes/5.1/README.md | 5 +++ 4 files changed, 144 insertions(+) create mode 100644 release-notes/5.1/5.1.5.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 129ca46b22..afcd9d2810 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,72 @@ 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 5.1.5] - 2024-01-29 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed connection to unsubscribe from transaction completion events before returning it to the connection pool [#2321](https://github.com/dotnet/SqlClient/pull/2321) +- Fixed InvalidCastException when reading an Always Encrypted date or time column [#2324](https://github.com/dotnet/SqlClient/pull/2324) + +### Changed + +- Changed Microsoft.IdentityModel.JsonWebTokens and Microsoft.IdentityModel.Protocols.OpenIdConnect version 6.24.0 to 6.35.0 [#2320](https://github.com/dotnet/SqlClient/pull/2320) to address [CVE-2024-21319](https://www.cve.org/CVERecord?id=CVE-2024-21319) + +## [Stable release 5.1.4] - 2024-01-09 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed a deadlock problem for distributed transactions when on .NET. + +### Changed + +- Upgraded `Azure.Identity` dependency version to [1.10.3](https://www.nuget.org/packages/Azure.Identity/1.10.3) to address [CVE-2023-36414](https://github.com/advisories/GHSA-5mfx-4wcx-rv27). + +## [Stable release 5.1.3] - 2024-01-09 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed encryption downgrade issue. [CVE-2024-0056](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-0056) +- Fixed certificate chain validation logic flow. + +## [Stable release 5.1.2] - 2023-10-26 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed access violation when using SQL Express user instance. [#2101](https://github.com/dotnet/SqlClient/pull/2101) +- Fixed Always Encrypted secure enclave retry logic for async queries. [#1988](https://github.com/dotnet/SqlClient/pull/1988) +- Fixed LocalDb and managed SNI by improving the error messages and avoid falling back to the local service. [#2129](https://github.com/dotnet/SqlClient/pull/2129) +- Fixed .NET and .NET Standard file version. [2093](https://github.com/dotnet/SqlClient/pull/2093) +- Fixed non-string values and `SqlConnectionStringBuilder` property indexer issue. [#2018](https://github.com/dotnet/SqlClient/pull/2018) +- Fixed `SqlConnectionEncryptOption` type conversion by introducing the `SqlConnectionEncryptOptionConverter` attribute when using **appsettings.json** files. [#2057](https://github.com/dotnet/SqlClient/pull/2057) +- Fixed Transient fault handling issue with `OpenAsync`. [#1983](https://github.com/dotnet/SqlClient/pull/1983) +- Fixed activity correlator to continue use of same GUID for connection activity. [#1997](https://github.com/dotnet/SqlClient/pull/1997) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.1.1`. [#2123](https://github.com/dotnet/SqlClient/pull/2123) + +## [Stable release 5.1.1] - 2023-03-28 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed an incorrect exception when a symmetric key fails to decrypt a column using Always Encrypted. [#1968](https://github.com/dotnet/SqlClient/pull/1968) +- Fixed `TransactionScope` connection issue when `Enlist` is `enabled`, `Pooling` is `disabled`, and `Network Connection Type` is set to `Redirect`. [#1967](https://github.com/dotnet/SqlClient/pull/1967) +- Fixed throttling of token requests by calling `AcquireTokenSilent`. [#1966](https://github.com/dotnet/SqlClient/pull/1966) +- Fixed TDS RPC error on large queries in `SqlCommand.ExecuteReaderAsync`. [#1965](https://github.com/dotnet/SqlClient/pull/1965) +- Fixed `NullReferenceException` in `GetBytesAsync`. [#1964](https://github.com/dotnet/SqlClient/pull/1964) + ## [Stable release 5.1.0] - 2023-01-19 This update brings the below changes over the previous release: diff --git a/release-notes/5.1/5.1.5.md b/release-notes/5.1/5.1.5.md new file mode 100644 index 0000000000..a272ffa527 --- /dev/null +++ b/release-notes/5.1/5.1.5.md @@ -0,0 +1,68 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.5 released 29 January 2024 + +This update includes the following changes over the previous release: + +### Fixed + +- Fixed connection to unsubscribe from transaction completion events before returning it to the connection pool [#2321](https://github.com/dotnet/SqlClient/pull/2321) +- Fixed InvalidCastException when reading an Always Encrypted date or time column [#2324](https://github.com/dotnet/SqlClient/pull/2324) + +### Changed + +- Changed Microsoft.IdentityModel.JsonWebTokens and Microsoft.IdentityModel.Protocols.OpenIdConnect version 6.24.0 to 6.35.0 [#2320](https://github.com/dotnet/SqlClient/pull/2320) to address [CVE-2024-21319](https://www.cve.org/CVERecord?id=CVE-2024-21319) + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET 6.0+ (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 5.1.1 +- Azure.Identity 1.10.3 +- Microsoft.Identity.Client 4.56.2 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Text.Encoding.Web 6.0.0 + +#### .NET + +- Microsoft.Data.SqlClient.SNI 5.1.1 +- Azure.Identity 1.10.3 +- Microsoft.Identity.Client 4.56.2 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI 5.1.1 +- Azure.Identity 1.10.3 +- Microsoft.Identity.Client 4.56.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.md b/release-notes/5.1/5.1.md index 6f6687b5d5..09b83805c2 100644 --- a/release-notes/5.1/5.1.md +++ b/release-notes/5.1/5.1.md @@ -4,6 +4,11 @@ The following Microsoft.Data.SqlClient 5.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2024/01/29 | 5.1.5 | [release notes](5.1.5.md) | +| 2024/01/09 | 5.1.4 | [release notes](5.1.4.md) | +| 2024/01/09 | 5.1.3 | [release notes](5.1.3.md) | +| 2023/10/26 | 5.1.2 | [release notes](5.1.2.md) | +| 2023/03/28 | 5.1.1 | [release notes](5.1.1.md) | | 2023/01/19 | 5.1.0 | [release notes](5.1.0.md) | The following Microsoft.Data.SqlClient 5.1 preview releases have been shipped: diff --git a/release-notes/5.1/README.md b/release-notes/5.1/README.md index 6f6687b5d5..09b83805c2 100644 --- a/release-notes/5.1/README.md +++ b/release-notes/5.1/README.md @@ -4,6 +4,11 @@ The following Microsoft.Data.SqlClient 5.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2024/01/29 | 5.1.5 | [release notes](5.1.5.md) | +| 2024/01/09 | 5.1.4 | [release notes](5.1.4.md) | +| 2024/01/09 | 5.1.3 | [release notes](5.1.3.md) | +| 2023/10/26 | 5.1.2 | [release notes](5.1.2.md) | +| 2023/03/28 | 5.1.1 | [release notes](5.1.1.md) | | 2023/01/19 | 5.1.0 | [release notes](5.1.0.md) | The following Microsoft.Data.SqlClient 5.1 preview releases have been shipped: From b4d40de3f9293cc50e4a67147d6716a893e9f9a6 Mon Sep 17 00:00:00 2001 From: dauinsight <145612907+dauinsight@users.noreply.github.com> Date: Tue, 7 May 2024 09:29:40 -0700 Subject: [PATCH 25/76] [5.1.6] Test | Updating tests to acquire token from user-assigned managed identity (#2360) (#2473) (#2478) * Remove test reference to deprecated ADAL library (#2360) * Test | Updating tests to acquire token from user-assigned managed identity (#2473) Co-authored-by: David Engel --------- Co-authored-by: David Engel Co-authored-by: Javad Rahnama Co-authored-by: David Engel --- BUILDGUIDE.md | 2 - .../AlwaysEncrypted/AKVUnitTests.cs | 13 ++-- .../TestFixtures/Setup/CertificateUtility.cs | 3 +- .../ManualTests/DataCommon/AADUtility.cs | 14 ---- .../ManualTests/DataCommon/DataTestUtility.cs | 16 +++-- .../SqlClientCustomTokenCredential.cs | 64 +++++-------------- ....Data.SqlClient.ManualTesting.Tests.csproj | 4 -- .../Config.cs | 2 - .../config.default.json | 2 - tools/props/Versions.props | 2 - 10 files changed, 33 insertions(+), 89 deletions(-) diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index 47cacc9fb7..e5f39c1cd9 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -176,8 +176,6 @@ Manual Tests require the below setup to run: |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}_ | |SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `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.| |LocalDbSharedInstanceName | (Optional) If LocalDB testing is supported and the instance is shared, this property configures the name of the shared instance of LocalDB to connect to. | Name of shared instance of LocalDB. | diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs index a9d69cd6e2..893c50799b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs @@ -3,12 +3,10 @@ // See the LICENSE file in the project root for more information. using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; -using Azure.Identity; using Xunit; using Azure.Security.KeyVault.Keys; using System.Reflection; using System; -using System.Linq; using System.Collections.Generic; using System.Threading; using System.Diagnostics.Tracing; @@ -86,8 +84,7 @@ public static void TokenCredentialTest() Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid(); using DataTestUtility.AKVEventListener AKVListener = new(); - ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret); - SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential); + SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential()); byte[] encryptedCek = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey); byte[] decryptedCek = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCek); @@ -104,8 +101,7 @@ public static void TokenCredentialRotationTest() // SqlClientCustomTokenCredential implements a legacy authentication callback to request the access token from the client-side. SqlColumnEncryptionAzureKeyVaultProvider oldAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential()); - ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret); - SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential); + SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential()); byte[] encryptedCekWithNewProvider = newAkvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey); byte[] decryptedCekWithOldProvider = oldAkvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCekWithNewProvider); @@ -129,15 +125,14 @@ public static void ReturnSpecifiedVersionOfKeyWhenItIsNotTheMostRecentVersion() { string keyName = keyPathUri.Segments[2]; string keyVersion = keyPathUri.Segments[3]; - ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret); - KeyClient keyClient = new KeyClient(vaultUri, clientSecretCredential); + KeyClient keyClient = new KeyClient(vaultUri, DataTestUtility.GetTokenCredential()); KeyVaultKey currentVersionKey = keyClient.GetKey(keyName); KeyVaultKey specifiedVersionKey = keyClient.GetKey(keyName, keyVersion); //If specified versioned key is the most recent version of the key then we cannot test. if (!KeyIsLatestVersion(specifiedVersionKey, currentVersionKey)) { - SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(clientSecretCredential); + SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential()); // Perform an operation to initialize the internal caches azureKeyProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVOriginalUrl, EncryptionAlgorithm, s_columnEncryptionKey); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs index 1c054f3769..3d3c717b31 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs @@ -141,8 +141,7 @@ internal static X509Certificate2 CreateCertificate() private static async Task SetupAKVKeysAsync() { - ClientSecretCredential clientSecretCredential = new ClientSecretCredential(DataTestUtility.AKVTenantId, DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret); - KeyClient keyClient = new KeyClient(DataTestUtility.AKVBaseUri, clientSecretCredential); + KeyClient keyClient = new KeyClient(DataTestUtility.AKVBaseUri, DataTestUtility.GetTokenCredential()); AsyncPageable keys = keyClient.GetPropertiesOfKeysAsync(); IAsyncEnumerator enumerator = keys.GetAsyncEnumerator(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs index 6abff25d13..7111ecbbc8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/AADUtility.cs @@ -7,25 +7,11 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using Microsoft.IdentityModel.Clients.ActiveDirectory; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class AADUtility { - public static async Task AzureActiveDirectoryAuthenticationCallback(string authority, string resource, string scope) - { - var authContext = new AuthenticationContext(authority); - ClientCredential clientCred = new ClientCredential(DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret); - AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred); - if (result == null) - { - throw new Exception($"Failed to retrieve an access token for {resource}"); - } - - return result.AccessToken; - } - public static async Task GetManagedIdentityToken(string clientId = null) => await new MockManagedIdentityTokenProvider().AcquireTokenAsync(clientId).ConfigureAwait(false); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index c92425561c..3b09804a82 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -20,6 +20,9 @@ using Xunit; using System.Net.NetworkInformation; using System.Text; +using System.Security.Principal; +using Azure.Identity; +using Azure.Core; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { @@ -39,8 +42,6 @@ public static class DataTestUtility public static readonly string AKVUrl = null; public static readonly string AKVOriginalUrl = null; 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 readonly string LocalDbSharedInstanceName = null; public static List AEConnStrings = new List(); @@ -139,8 +140,6 @@ static DataTestUtility() } AKVTenantId = c.AzureKeyVaultTenantId; - AKVClientId = c.AzureKeyVaultClientId; - AKVClientSecret = c.AzureKeyVaultClientSecret; if (EnclaveEnabled) { @@ -337,7 +336,14 @@ public static bool IsNotAzureServer() // Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/17858869-support-always-encrypted-in-sql-data-warehouse public static bool IsAKVSetupAvailable() { - return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(AKVClientId) && !string.IsNullOrEmpty(AKVClientSecret) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse(); + return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(UserManagedIdentityClientId) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse(); + } + + private static readonly DefaultAzureCredential s_defaultCredential = new(new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserManagedIdentityClientId }); + + public static TokenCredential GetTokenCredential() + { + return s_defaultCredential; } public static bool IsTargetReadyForAeWithKeyStore() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs index 23ed76f81e..977bc53257 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs @@ -3,20 +3,20 @@ // See the LICENSE file in the project root for more information. using System; -using System.IdentityModel.Tokens.Jwt; +using System.Collections.Concurrent; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Azure.Core; -using Microsoft.IdentityModel.Clients.ActiveDirectory; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using Azure.Identity; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class SqlClientCustomTokenCredential : TokenCredential { + private const string DEFAULT_PREFIX = "/.default"; + string _authority = ""; string _resource = ""; string _akvUrl = ""; @@ -70,40 +70,8 @@ private async Task AcquireTokenAsync() _akvUrl = DataTestUtility.AKVUrl; } - string strAccessToken = await AzureActiveDirectoryAuthenticationCallback(_authority, _resource); - DateTime expiryTime = InterceptAccessTokenForExpiry(strAccessToken); - return new AccessToken(strAccessToken, new DateTimeOffset(expiryTime)); - } - - private DateTime InterceptAccessTokenForExpiry(string accessToken) - { - if (null == accessToken) - { - throw new ArgumentNullException(accessToken); - } - - var jwtHandler = new JwtSecurityTokenHandler(); - var jwtOutput = string.Empty; - - // Check Token Format - if (!jwtHandler.CanReadToken(accessToken)) - throw new FormatException(accessToken); - - JwtSecurityToken token = jwtHandler.ReadJwtToken(accessToken); - - // Re-serialize the Token Headers to just Key and Values - var jwtHeader = JsonConvert.SerializeObject(token.Header.Select(h => new { h.Key, h.Value })); - jwtOutput = $"{{\r\n\"Header\":\r\n{JToken.Parse(jwtHeader)},"; - - // Re-serialize the Token Claims to just Type and Values - var jwtPayload = JsonConvert.SerializeObject(token.Claims.Select(c => new { c.Type, c.Value })); - jwtOutput += $"\r\n\"Payload\":\r\n{JToken.Parse(jwtPayload)}\r\n}}"; - - // Output the whole thing to pretty JSON object formatted. - string jToken = JToken.Parse(jwtOutput).ToString(Formatting.Indented); - JToken payload = JObject.Parse(jToken).GetValue("Payload"); - - return new DateTime(1970, 1, 1).AddSeconds((long)payload[4]["Value"]); + AccessToken accessToken = await AzureActiveDirectoryAuthenticationCallback(_authority, _resource); + return accessToken; } private static string ValidateChallenge(string challenge) @@ -127,16 +95,18 @@ private static string ValidateChallenge(string challenge) /// Authorization URL /// Resource /// - public static async Task AzureActiveDirectoryAuthenticationCallback(string authority, string resource) + public static async Task AzureActiveDirectoryAuthenticationCallback(string authority, string resource) { - var authContext = new AuthenticationContext(authority); - ClientCredential clientCred = new ClientCredential(DataTestUtility.AKVClientId, DataTestUtility.AKVClientSecret); - AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred); - if (result == null) - { - throw new InvalidOperationException($"Failed to retrieve an access token for {resource}"); - } - return result.AccessToken; + using CancellationTokenSource cts = new(); + cts.CancelAfter(30000); // Hard coded for tests + string[] scopes = new string[] { resource + DEFAULT_PREFIX }; + TokenRequestContext tokenRequestContext = new(scopes); + int separatorIndex = authority.LastIndexOf('/'); + string authorityHost = authority.Remove(separatorIndex + 1); + string audience = authority.Substring(separatorIndex + 1); + TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions() { AuthorityHost = new Uri(authorityHost) }; + AccessToken accessToken = await DataTestUtility.GetTokenCredential().GetTokenAsync(tokenRequestContext, cts.Token).ConfigureAwait(false); + return accessToken; } } } 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 9f0cb5a11b..f570fad10f 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 @@ -314,18 +314,14 @@ - - - - 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 47aad75eec..5c3ca85072 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 @@ -22,8 +22,6 @@ public class Config public string AADServicePrincipalSecret = null; public string AzureKeyVaultURL = null; public string AzureKeyVaultTenantId = null; - public string AzureKeyVaultClientId = null; - public string AzureKeyVaultClientSecret = null; public string LocalDbAppName = null; public string LocalDbSharedInstanceName = null; public bool EnclaveEnabled = false; 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 37f7c9184d..b83ffe8883 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 @@ -13,8 +13,6 @@ "AADServicePrincipalSecret": "", "AzureKeyVaultURL": "", "AzureKeyVaultTenantId": "", - "AzureKeyVaultClientId": "", - "AzureKeyVaultClientSecret": "", "SupportsIntegratedSecurity": true, "LocalDbAppName": "", "LocalDbSharedInstanceName": "", diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 1e8cf6a3b0..bdb5d55136 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -62,12 +62,10 @@ 3.1.6 - 5.2.9 17.4.1 13.0.1 4.3.0 6.0.1 - 6.35.0 2.4.2 2.4.5 7.0.0-beta.22316.1 From 399412274793a7b28b9c37c9a0a79163bb80fab9 Mon Sep 17 00:00:00 2001 From: dauinsight <145612907+dauinsight@users.noreply.github.com> Date: Wed, 8 May 2024 09:28:59 -0700 Subject: [PATCH 26/76] SDL | Changing ReadXml to a more secure overload. (#2147) (#2490) Co-authored-by: Javad --- .../Microsoft/Data/ProviderBase/DbMetaDataFactory.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbMetaDataFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbMetaDataFactory.cs index 6e907d26e1..c3c34c702b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbMetaDataFactory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbMetaDataFactory.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Globalization; using System.IO; +using System.Xml; namespace Microsoft.Data.ProviderBase { @@ -499,9 +500,14 @@ private void LoadDataSetFromXml(Stream XmlStream) { _metaDataCollectionsDataSet = new DataSet { - Locale = System.Globalization.CultureInfo.InvariantCulture + Locale = CultureInfo.InvariantCulture + }; + XmlReaderSettings settings = new() + { + XmlResolver = null }; - _metaDataCollectionsDataSet.ReadXml(XmlStream); + using XmlReader reader = XmlReader.Create(XmlStream, settings); + _metaDataCollectionsDataSet.ReadXml(reader); } protected virtual DataTable PrepareCollection(string collectionName, string[] restrictions, DbConnection connection) From 82294a9622294613f9e4b0a4c2ce72dc993ebf1d Mon Sep 17 00:00:00 2001 From: Daniel Au Date: Thu, 16 May 2024 22:17:15 +0000 Subject: [PATCH 27/76] Merged PR 4540: eng | Add 5.1 build YAML pipeline Successful run: https://sqlclientdrivers.visualstudio.com/ADO.Net/_build/results?buildId=87027&view=results Related work items: #28132 --- .config/CredScanSuppressions.json | 25 ++ .config/PolicheckExclusions.xml | 5 + .config/tsaoptions.json | 14 + .../jobs/build-signed-akv-package-job.yml | 80 +++++ .../jobs/build-signed-package-job.yml | 62 ++++ .../jobs/run-tests-package-reference-job.yml | 58 ++++ .../jobs/validate-signed-package-job.yml | 296 ++++++++++++++++++ ...ld-all-configurations-signed-dlls-step.yml | 61 ++++ .../build-and-run-tests-netcore-step.yml | 80 +++++ .../steps/build-and-run-tests-netfx-step.yml | 79 +++++ .../templates/steps/code-analyze-step.yml | 51 +++ .../steps/copy-dlls-for-test-step.yml | 107 +++++++ .../steps/esrp-code-signing-step.yml | 106 +++++++ .../steps/generate-nuget-package-step.yml | 42 +++ .../common/templates/steps/pre-build-step.yml | 20 ++ .../templates/steps/prepare-test-db-step.yml | 26 ++ .../templates/steps/publish-symbols-step.yml | 64 ++++ .../steps/update-config-file-step.yml | 35 +++ .../update-nuget-config-local-feed-step.yml | 78 +++++ .../dotnet-sqlclient-signing-pipeline.yml | 159 ++++++++++ eng/pipelines/libraries/akv-variables.yml | 21 ++ eng/pipelines/libraries/build-variables.yml | 10 + eng/pipelines/libraries/common-variables.yml | 19 ++ .../libraries/mds-validation-variables.yml | 34 ++ eng/pipelines/libraries/mds-variables.yml | 24 ++ eng/pipelines/libraries/variables.yml | 17 + .../Data/SqlClient/TdsParserHelperClasses.cs | 10 +- .../SqlClient/TdsParserStateObjectNative.cs | 5 +- .../VirtualSecureModeEnclaveProviderBase.cs | 8 +- ...waysEncrypted.AzureKeyVaultProvider.nuspec | 38 +-- 30 files changed, 1608 insertions(+), 26 deletions(-) create mode 100644 .config/CredScanSuppressions.json create mode 100644 .config/PolicheckExclusions.xml create mode 100644 .config/tsaoptions.json create mode 100644 eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml create mode 100644 eng/pipelines/common/templates/jobs/build-signed-package-job.yml create mode 100644 eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml create mode 100644 eng/pipelines/common/templates/jobs/validate-signed-package-job.yml create mode 100644 eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml create mode 100644 eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml create mode 100644 eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml create mode 100644 eng/pipelines/common/templates/steps/code-analyze-step.yml create mode 100644 eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml create mode 100644 eng/pipelines/common/templates/steps/esrp-code-signing-step.yml create mode 100644 eng/pipelines/common/templates/steps/generate-nuget-package-step.yml create mode 100644 eng/pipelines/common/templates/steps/pre-build-step.yml create mode 100644 eng/pipelines/common/templates/steps/prepare-test-db-step.yml create mode 100644 eng/pipelines/common/templates/steps/publish-symbols-step.yml create mode 100644 eng/pipelines/common/templates/steps/update-config-file-step.yml create mode 100644 eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml create mode 100644 eng/pipelines/dotnet-sqlclient-signing-pipeline.yml create mode 100644 eng/pipelines/libraries/akv-variables.yml create mode 100644 eng/pipelines/libraries/build-variables.yml create mode 100644 eng/pipelines/libraries/common-variables.yml create mode 100644 eng/pipelines/libraries/mds-validation-variables.yml create mode 100644 eng/pipelines/libraries/mds-variables.yml create mode 100644 eng/pipelines/libraries/variables.yml diff --git a/.config/CredScanSuppressions.json b/.config/CredScanSuppressions.json new file mode 100644 index 0000000000..ffc7e87789 --- /dev/null +++ b/.config/CredScanSuppressions.json @@ -0,0 +1,25 @@ +{ + "tool": "Credential Scanner", + "suppressions": [ + { + "file": "src/Microsoft.Data.SqlClient/tests/Docker/DockerLinuxTest/Program.cs", + "justification": "Test projects should be skipped" + }, + { + "file": "src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs", + "justification": "Test projects should be skipped" + }, + { + "file": "src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TdsServerCertificate.pfx", + "justification": "Test projects should be skipped" + }, + { + "file": "src/docker-compose.yml", + "justification": "Docker test project should be excluded" + }, + { + "file": "doc/samples/SqlConnectionStringBuilder.cs", + "justification": "Documentation could include sample data and can be ignored" + } + ] +} diff --git a/.config/PolicheckExclusions.xml b/.config/PolicheckExclusions.xml new file mode 100644 index 0000000000..d8c47d335d --- /dev/null +++ b/.config/PolicheckExclusions.xml @@ -0,0 +1,5 @@ + + SRC/MICROSOFT.DATA.SQLCLIENT/TESTS + .YML|.MD|.SQL + NOTICE.TXT + \ No newline at end of file diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json new file mode 100644 index 0000000000..c9e3a2cfb6 --- /dev/null +++ b/.config/tsaoptions.json @@ -0,0 +1,14 @@ +{ + "instanceUrl": "https://sqlclientdrivers.visualstudio.com/", + "projectName": "ADO.Net", + "areaPath": "ADO.Net", + "iterationPath": "ADO.Net\\TSA\\SqlClient", + "notificationAliases": [ "SqlClient@microsoft.com" ], + "repositoryName": "SqlClient", + "codebaseName": "SqlClient", + "allTools": true, + "template": "MSDATA_RevolutionR", + "language": "csharp", + "includePathPatterns": "src/Microsoft.Data.SqlClient/*, src/Microsoft.SqlServer.Server/*, tools/*", + "excludePathPatterns": "src/Microsoft.Data.SqlClient/tests/*" +} diff --git a/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml b/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml new file mode 100644 index 0000000000..228195aa61 --- /dev/null +++ b/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml @@ -0,0 +1,80 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: symbolsFolder + type: string + default: symbols + + - name: softwareFolder + type: string + default: software + + - name: publishSymbols + type: boolean + +jobs: +- job: build_signed_akv_package + pool: + type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs + + variables: + - template: ../../../libraries/variables.yml@self + - name: PublishSymbols + value: ${{ parameters['PublishSymbols'] }} + + steps: + - script: SET + displayName: 'Print Environment Variables' + + - template: ../steps/build-all-configurations-signed-dlls-step.yml@self + parameters: + product: AKV + nugetPackageRefVersion: $(MDS_PackageRef_Version) + AssemblyFileVersion: $(AKVAssemblyFileVersion) + + - template: ../steps/code-analyze-step.yml@self + parameters: + analyzeType: all + product: AKV + nugetPackageRefVersion: $(MDS_PackageRef_Version) + + - template: ../steps/esrp-code-signing-step.yml@self + parameters: + artifactType: dll + + - template: ../steps/generate-nuget-package-step.yml@self + parameters: + OutputDirectory: $(artifactDirectory) + nuspecPath: ${{variables.akvNuspecPath }} + NugetPackageVersion: ${{variables.AKVNuGetPackageVersion }} + referenceType: package + + - template: ../steps/esrp-code-signing-step.yml@self + parameters: + artifactType: pkg + + - template: ../steps/copy-dlls-for-test-step.yml@self + parameters: + product: AKV + referenceType: package + + # Publish symbols to private server + - template: ../steps/publish-symbols-step.yml@self + parameters: + SymAccount: $(PrivateSymAccount) + referenceType: package + symbolsVersion: ${{variables.AKVNuGetPackageVersion }} + product: AKV + publishSymbols: ${{ parameters['PublishSymbols'] }} + + # Publish symbols to public server + - template: ../steps/publish-symbols-step.yml@self + parameters: + SymAccount: $(PublicSymAccount) + referenceType: package + symbolsVersion: ${{variables.AKVNuGetPackageVersion }} + product: AKV + publishSymbols: ${{ parameters['PublishSymbols'] }} diff --git a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml new file mode 100644 index 0000000000..ad4344dd73 --- /dev/null +++ b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml @@ -0,0 +1,62 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: symbolsFolder + type: string + default: symbols + + - name: softwareFolder + type: string + default: software + + - name: publishSymbols + type: boolean + +jobs: +- job: build_signed_package + pool: + type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs + + variables: + - template: ../../../libraries/variables.yml@self + + steps: + - script: SET + displayName: 'Print Environment Variables' + + - template: ../steps/build-all-configurations-signed-dlls-step.yml@self + + - template: ../steps/code-analyze-step.yml@self + parameters: + analyzeType: all + + - template: ../steps/esrp-code-signing-step.yml@self + parameters: + artifactType: dll + + - template: ../steps/generate-nuget-package-step.yml@self + parameters: + OutputDirectory: $(artifactDirectory) + + - template: ../steps/esrp-code-signing-step.yml@self + parameters: + artifactType: pkg + + - template: ../steps/copy-dlls-for-test-step.yml@self + parameters: + product: MDS + + # Publish symbols to private server + - template: ../steps/publish-symbols-step.yml@self + parameters: + SymAccount: $(PrivateSymAccount) + publishSymbols: ${{ parameters['PublishSymbols'] }} + + # Publish symbols to public server + - template: ../steps/publish-symbols-step.yml@self + parameters: + SymAccount: $(PublicSymAccount) + publishSymbols: ${{ parameters['PublishSymbols'] }} diff --git a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml new file mode 100644 index 0000000000..f764a55201 --- /dev/null +++ b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml @@ -0,0 +1,58 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: downloadPackageStep + type: step + default: + script: echo + + - name: packageFolderName + type: string + default: drop_build_build_signed_package + + - name: dependsOn + type: string + default: empty + +jobs: +- job: run_tests_package_reference + ${{ if ne(parameters.dependsOn, 'empty')}}: + dependsOn: '${{parameters.dependsOn }}' + pool: + type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs + isCustom: true + name: ADO-1ES-Pool + vmImage: 'ADO-MMS22-SQL19' + + variables: # More settings at https://aka.ms/obpipelines/yaml/jobs + - template: ../../../libraries/mds-validation-variables.yml@self + + steps: + - template: ../steps/pre-build-step.yml + + - ${{parameters.downloadPackageStep }} + + - template: ../steps/update-nuget-config-local-feed-step.yml + parameters: + downloadedNugetPath: $(Pipeline.Workspace)\${{parameters.packageFolderName }} + + - template: ../steps/update-config-file-step.yml + parameters: + TCPConnectionString: $(SQL_TCP_CONN_STRING) + NPConnectionString: $(SQL_NP_CONN_STRING) + SupportsIntegratedSecurity: false + + - template: ../steps/prepare-test-db-step.yml + +# build & test + - template: ../steps/build-and-run-tests-netfx-step.yml + parameters: + referenceType: Package + + - template: ../steps/build-and-run-tests-netcore-step.yml + parameters: + referenceType: Package + cleanFirst: true diff --git a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml new file mode 100644 index 0000000000..1a7ca1ae21 --- /dev/null +++ b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml @@ -0,0 +1,296 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: downloadPackageStep + type: step + default: + script: echo + + - name: packageFolderName + type: string + default: drop_build_build_signed_package + + - name: dependsOn + type: string + default: '' + + - name: packageType + type: string + default: both + values: + - dll + - pdb + - both + + - name: assembly_file_version_netfx + type: string + default: $(AssemblyFileVersion) + + - name: assembly_file_version_core + type: string + default: $(AssemblyFileVersion) + + - name: current_netfx_target_framework + type: string + default: $(CurrentNetFxVersion) + +jobs: +- job: validate_signed_package + ${{ if ne(parameters.dependsOn, '')}}: + dependsOn: '${{parameters.dependsOn }}' + pool: + type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs + isCustom: true + name: ADO-1ES-Pool + vmImage: 'ADO-MMS22-SQL19' + + variables: # More settings at https://aka.ms/obpipelines/yaml/jobs + - template: ../../../libraries/mds-validation-variables.yml@self + + - name: pathToDownloadedNuget # path to the downloaded nuget files + value: $(Pipeline.Workspace)\${{parameters.packageFolderName }} + + steps: + - script: SET + displayName: 'Print Environment Variables' + + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet ' + + - powershell: | + #Sets Variables for AssemblyFileVersion, AssemblyVersion and NugetPackageVersion + + [Xml] $versionprops = Get-Content -Path ".\tools\props\Versions.props" + Write-Host $versionprops.Project.PropertyGroup[0].AssemblyFileVersion + + $AssemblyVersion = $versionprops.Project.PropertyGroup[0].AssemblyVersion + + Write-Host "##vso[task.setvariable variable=ASSEMBLY_VERSION;]$AssemblyVersion" + displayName: 'Update assembly version property' + + - powershell: | + # Displays the paths of all the local cache directories + nuget locals all -List + + #Clears all files from all local cache directories + nuget locals all -Clear + displayName: 'Clear local cache' + + - ${{parameters.downloadPackageStep }} + + - powershell: | + # Install nuget package + Install-Package -Name "Microsoft.Data.SqlClient" -Destination "$(TempFolderName)" -Force -Source $(pathToDownloadedNuget) -SkipDependencies + + Write-Host "--------------------------------------------------" + Write-Host '$(TempFolderName)' + ls $(TempFolderName) + Write-Host "--------------------------------------------------" + displayName: 'Extract Nuget in temp folder' + + - powershell: | + # Artifact is stored in the Nuget folder + $packageType = '${{parameters.packageType}}' + + Write-Host "--------------------------------------------------" + Write-Host "This will verify the artifact signature" -ForegroundColor Green + Write-Host "--------------------------------------------------" + + nuget verify -All $(pathToDownloadedNuget)\*.nupkg + displayName: 'Verify nuget signature' + + - powershell: | + # Checks the expected folder names such as lib, ref, runtimes + Get-ChildItem -Path $(extractedNugetPath) -Directory | select Name | foreach { + if('$(expectedFolderNames)'.contains($_.Name)){ + Write-Host expected folder name verfied: $_.Name + } + } + displayName: 'Check expected folder names' + + - powershell: | + # Checks the version of DotNetFramework, NetStandard and NetCore + $countErr = 0 + $countPass = 0 + $excludNamesFromRuntimeFolder = 'lib','win','unix' + + Get-ChildItem -Path $(extractedNugetPath) -Directory | foreach { + $parentname=$_.Name + Write-Host $_.FullName -ForegroundColor yellow + + if($_.Name -ne 'runtimes') { + Get-ChildItem -Path $_.FullName -Directory | select Name | foreach { + if('$(expectedDotnetVersions)'.Contains($_.Name)){ + Write-Host "`tExpected version verified in $parentname": $_.Name -ForegroundColor green + $countPass += 1 + } + else{ + Write-Host "`tUnexpected version detected in $parentname": $_.Name + $countErr += 1 + } + } + } + + elseif ($_.Name -eq 'runtimes'){ + Get-ChildItem -Depth 3 -Path $_.FullName -Exclude $excludNamesFromRuntimeFolder -Directory | foreach{ + if('$(expectedDotnetVersions)'.Contains($_.Name)){ + Write-Host "`tExpected version verfied in $parentname": $_.Name + $countPass += 1 + } + else{ + Write-Host "`tUnexpected version detected": $_.Name -ForegroundColor Red + $countErr += 1 + } + } + } + else{ + Write-Host "`tUnknown folder " $_.Name -ForegroundColor Red + Exit -1 + } + } + + Write-Host "_______________" + Write-Host "Expected: $countPass" + Write-Host "Unexpected: $countErr" + Write-Host "_______________" + if ($countErr -ne 0) + { + Write-Host "Unexpected versions are detected!" -ForegroundColor Red + Exit -1 + } + displayName: 'Check Expected framework' + + - powershell: | + # list all the child items of created temp folder + + #Verify all DLLs unzipped match "expected" hierarchy + + foreach( $folderName in (Get-ChildItem -Path $(extractedNugetPath) -Directory).Name) + { + # List all Childerns of the Path + Get-ChildItem -Path $(extractedNugetPath)\$folderName -Recurse -File + $subFiles = Get-ChildItem -Path $(extractedNugetPath)\$folderName -Recurse -File + + foreach($file in $subFiles) + { + if($subFiles[0].Name -like "*.dll" -and $subFiles[1].Name -like "*.pdb" ) + { + Write-Host $subFiles[0].Name -ForegroundColor Green + Write-Host $subFiles[1].Name -ForegroundColor Green + if(($folderName -eq 'lib') -or ($folderName -eq 'ref')) + { + if($subFiles[2].Name -like "*.xml") + { + Write-Host $subFiles[2].Name -ForegroundColor Green + } + else + { + $subFiles[2].Name + Write-Host "Expected file pattern did not match to *.xml" -ForegroundColor Red + Exit -1 + } + } + } + else + { + $subFiles[0].Name + $subFiles[1].Name + Write-Host "Expected file pattern did not match to *.dll, *.pdb" -ForegroundColor Red + Exit -1 + } + } + } + displayName: 'Verify all DLLs unzipped match "expected" hierarchy' + + - powershell: | + # Verify all dlls status are Valid + + $dlls = Get-ChildItem -Path $(extractedNugetPath) -Recurse -Include *.dll + foreach ($status in $dlls | Get-AuthenticodeSignature) + { + if ($status.Status -eq "Valid") + { + Write-Host $status.Status $status.Path + } + else + { + Write-Host "dll status of '$status.Path' is not valid!" -ForegroundColor Red + $status + Exit -1 + } + } + displayName: 'Verify all dlls status are Valid' + + - powershell: | + # This will check for ProductVersion and FileVersion + # For NetFx we have a different FileVersion, but product versions are all the same some may have and extra numbering at the end. we only check for # first parts + + foreach ( $pVersion in Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object versioninfo ) + { + if ($pVersion.ProductVersion -Like '$(ProductVersion)*') + { + Write-Host Valid Product Version:"$pVersion.ProductVersion" $pVersion.ProductVersion detected for $pVersion.FileName -ForegroundColor Green + } + else + { + Write-Host "Wrong ProductVersion detected. Expected: '$(ProductVersion)', but Detected: "$pVersion.ProductVersion"" + Exit -1 + } + + if($pVersion.FileName -like '*lib\${{parameters.current_netfx_target_framework }}*'){ + + if($pVersion.FileVersion -eq '${{parameters.assembly_file_version_netfx }}') + { + Write-Host 'Correct File version Detected for net46,' $pVersion.FileVersion -ForegroundColor Green + } + else + { + Write-Host 'Wrong File version Detected for net46,' $pVersion.FileVersion -ForegroundColor Red + Exit -1 + } + } + else + { + + if($pVersion.FileVersion -eq '${{parameters.assembly_file_version_core}}') + { + Write-Host 'Correct File version Detected for netcore and netstandard,' $pVersion.FileVersion -ForegroundColor Green + } + else + { + Write-Host 'Wrong File version Detected for netcore and netstandard and ref folder of netfx,' $pVersion.FileVersion -ForegroundColor Red + Exit -1 + } + } + } + + Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object versioninfo + displayName: 'Verify "File Version" matches provided pipeline variable "ASSEMBLY_FILE_VERSION" for DLLs' + + - powershell: | + #Change TestMicrosoftDataSqlClientVersion + + [Xml] $versionprops = Get-Content -Path "tools/props/Versions.props" + $versionpropspath = "tools\props\Versions.props" + $versionprops.Project.PropertyGroup[$versionprops.Project.PropertyGroup.Count-1].TestMicrosoftDataSqlClientVersion ="$(NugetPackageVersion)" + Write-Host "Saving Test nuget version at $rootfolder\props ...." -ForegroundColor Green + $versionprops.Save($versionpropspath) + + displayName: 'Modify TestMicrosoftDataSqlClientVersion' + + - powershell: | + #Change TestMicrosoftDataSqlClientVersion + + [Xml] $versionprops = Get-Content -Path "tools/props/Versions.props" + $AssemblyFileVersion = $versionprops.Project.PropertyGroup[0].AssemblyFileVersion + $AssemblyVersion = $versionprops.Project.PropertyGroup[0].AssemblyVersion + + if($AssemblyFileVersion -eq $AssemblyVersion) + { + Write-Host AssemblyFileVersion: $AssemblyFileVersion should not be equal to: $AssemblyVersion + Exit -1 + } + displayName: 'Check "AssemblyFileVersion" is not same as "AssemblyVersion" in version.props' diff --git a/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml b/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml new file mode 100644 index 0000000000..1b8401cbc3 --- /dev/null +++ b/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml @@ -0,0 +1,61 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: AssemblyFileVersion + type: string + default: $(AssemblyFileVersion) + + - name: Configuration + type: string + default: '$(Configuration)' + + - name: nugetPackageRefVersion + type: string + default: '' + + - name: product + default: MDS + values: + - MDS + - AKV + - MSS + +steps: +- task: DownloadSecureFile@1 + displayName: 'Download Key Pair' + inputs: + secureFile: netfxKeypair.snk + retryCount: 5 + +- ${{ if eq(parameters.product, 'MDS') }}: + - task: MSBuild@1 + displayName: 'BuildAllConfigurations using build.proj' + inputs: + solution: '**/build.proj' + configuration: '${{parameters.Configuration }}' + msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAllConfigurations -p:GenerateNuget=false -p:SignAssembly=true -p:AssemblyOriginatorKeyFile=$(Agent.TempDirectory)\netfxKeypair.snk' + +- ${{ if eq(parameters.product, 'AKV') }}: + - task: MSBuild@1 + displayName: 'BuildAKVNetStAllOS using build.proj' + inputs: + solution: '**/build.proj' + configuration: '$(Configuration)' + msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAKVNetStAllOS -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -p:SignAssembly=true -p:AssemblyOriginatorKeyFile=$(Agent.TempDirectory)\netfxKeypair.snk' + + - task: MSBuild@1 + displayName: 'BuildAKVNetFx using build.proj' + inputs: + solution: '**/build.proj' + configuration: '$(Configuration)' + msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAKVNetFx -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -p:SignAssembly=true -p:AssemblyOriginatorKeyFile=$(Agent.TempDirectory)\netfxKeypair.snk' + + - task: MSBuild@1 + displayName: 'BuildAKVNetCoreAllOS using build.proj' + inputs: + solution: '**/build.proj' + configuration: '$(Configuration)' + msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAKVNetCoreAllOS -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -p:SignAssembly=true -p:AssemblyOriginatorKeyFile=$(Agent.TempDirectory)\netfxKeypair.snk' diff --git a/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml b/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml new file mode 100644 index 0000000000..f747fa57e6 --- /dev/null +++ b/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml @@ -0,0 +1,80 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: TargetNetCoreVersion + type: string + default: $(TargetNetCoreVersion) + + - name: configuration + type: string + default: $(Configuration) + + - name: referenceType + default: Project + values: + - Project + - Package + + - name: NugetPackageVersion + type: string + default: $(NugetPackageVersion) + + - name: platform + type: string + default: $(Platform) + + - name: cleanFirst + type: boolean + default: false + + - name: TestTargetOS + type: string + default: Windowsnetcoreapp + values: + - Windowsnetfx + - Windowsnetcoreapp + - Unixnetcoreapp + + - name: retryCountOnManualTests + type: number + default: 2 + +steps: +- ${{ if eq(parameters.cleanFirst, true)}}: + - task: MSBuild@1 + displayName: 'Clean artifacts folder' + inputs: + solution: build.proj + msbuildArguments: '-t:clean' + +- task: MSBuild@1 + displayName: 'Build AKV Provider .NET' + inputs: + solution: build.proj + msbuildArchitecture: x64 + msbuildArguments: '-p:Configuration=${{parameters.configuration }} -t:BuildAKVNetCore -p:ReferenceType=${{parameters.referenceType }} ' + +- task: MSBuild@1 + displayName: 'MSBuild Build Tests for ${{parameters.TargetNetCoreVersion }}' + inputs: + solution: build.proj + msbuildArchitecture: x64 + msbuildArguments: '-t:BuildTestsNetCore -p:ReferenceType=${{parameters.referenceType }} -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} -p:Configuration=${{parameters.configuration }}' + +- task: DotNetCoreCLI@2 + displayName: 'Run Functional Tests for ${{parameters.TargetNetCoreVersion }}' + inputs: + command: test + projects: 'src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj' + arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.configuration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests"' + +- task: DotNetCoreCLI@2 + displayName: 'Run Manual Tests for ${{parameters.TargetNetCoreVersion }}' + inputs: + command: test + projects: 'src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj' + arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.configuration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} --no-build -v n --filter category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests --collect "Code Coverage"' + retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }} diff --git a/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml b/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml new file mode 100644 index 0000000000..ab77af3ee9 --- /dev/null +++ b/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml @@ -0,0 +1,79 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: TargetNetFxVersion + type: string + default: $(TargetNetFxVersion) + + - name: configuration + type: string + default: $(Configuration) + + - name: referenceType + default: Project + values: + - Project + - Package + + - name: NugetPackageVersion + type: string + default: $(NugetPackageVersion) + + - name: platform + type: string + default: $(Platform) + + - name: cleanFirst + type: boolean + default: false + + - name: TestTargetOS + type: string + default: Windowsnetfx + values: + - Windowsnetfx + - Windowsnetcoreapp + - Unixnetcoreapp + + - name: retryCountOnManualTests + type: number + default: 2 + +steps: +- ${{ if eq(parameters.cleanFirst, true)}}: + - task: MSBuild@1 + displayName: 'Clean artifacts folder' + inputs: + solution: build.proj + msbuildArguments: '-t:clean' + +- task: MSBuild@1 + displayName: 'Build AKV Provider .NET Framework' + inputs: + solution: build.proj + msbuildArchitecture: x64 + msbuildArguments: '-p:Configuration=${{parameters.configuration }} -t:BuildAKVNetFx -p:ReferenceType=${{parameters.referenceType }} ' + +- task: MSBuild@1 + displayName: 'MSBuild Build Tests for ${{parameters.TargetNetFxVersion }}' + inputs: + solution: build.proj + msbuildArguments: ' -t:BuildTestsNetFx -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:Configuration=${{parameters.configuration }} -p:Platform=${{parameters.platform }}' + +- task: DotNetCoreCLI@2 + displayName: 'Run Functional Tests for ${{parameters.TargetNetFxVersion }}' + inputs: + command: test + projects: 'src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj' + arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.configuration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"' + +- task: DotNetCoreCLI@2 + displayName: 'Run Manual Tests for ${{parameters.TargetNetFxVersion }}' + inputs: + command: test + projects: 'src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj' + arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.configuration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"' + retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }} diff --git a/eng/pipelines/common/templates/steps/code-analyze-step.yml b/eng/pipelines/common/templates/steps/code-analyze-step.yml new file mode 100644 index 0000000000..92be8eabf6 --- /dev/null +++ b/eng/pipelines/common/templates/steps/code-analyze-step.yml @@ -0,0 +1,51 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: analyzeType + values: + - roslyn + - inspect + - all + + - name: sourceRoot + type: string + default: $(REPOROOT) + + - name: nugetPackageRefVersion + type: string + default: '' + + - name: product + default: MDS + values: + - MDS + - AKV + - MSS + +steps: +- ${{ if or(eq(parameters.analyzeType, 'roslyn'), eq(parameters.analyzeType, 'all')) }}: + - ${{ if eq(parameters.product, 'MDS') }}: + - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3 + displayName: 'Guardian Dotnet Analyzers ' + inputs: + msBuildVersion: 17.0 + msBuildArchitecture: x64 + setupCommandlinePicker: vs2022 + msBuildCommandline: 'msbuild ${{parameters.sourceRoot}}\build.proj -p:configuration=Release -p:GenerateNuget=false -p:BuildTools=false' + - ${{ if eq(parameters.product, 'AKV') }}: + - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3 + displayName: 'Guardian Dotnet Analyzers ' + inputs: + msBuildVersion: 17.0 + msBuildArchitecture: x64 + setupCommandlinePicker: vs2022 + msBuildCommandline: 'msbuild ${{parameters.sourceRoot}}\build.proj -p:configuration=Release -p:GenerateNuget=false -p:BuildTools=false -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -t:BuildAKVNetCoreAllOS' + +- ${{ if or(eq(parameters.analyzeType, 'inspect'), eq(parameters.analyzeType, 'all')) }}: + - task: securedevelopmentteam.vss-secure-development-tools.build-task-codeinspector.CodeInspector@2 + displayName: 'Run Code Inspector' + inputs: + LogLevel: Error diff --git a/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml b/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml new file mode 100644 index 0000000000..ec1ac1f94f --- /dev/null +++ b/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml @@ -0,0 +1,107 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: Configuration + type: string + default: '$(Configuration)' + + - name: symbolsFolder + type: string + default: symbols + + - name: softwareFolder + type: string + default: software + + - name: referenceType + default: project + values: + - project + - package + + - name: listOfTF + type: object + default: + - net462 + - net6.0 + - netstandard2.0 + - netstandard2.1 + + - name: product + default: MDS + values: + - MDS + - AKV + - MSS + +steps: +- powershell: | + $software = '${{parameters.softwareFolder}}' + $symbols = '${{parameters.symbolsFolder}}' + + md $software + md $software\win + + md $symbols + md $symbols\win + displayName: 'Make base directories' + +- ${{ each targetFramework in parameters.listOfTF }}: + - ${{ if eq(parameters.product, 'MDS') }}: + - powershell: | + $software = '${{parameters.softwareFolder}}' + $tf = '${{ targetFramework }}' + md $software\win\$tf + + if ($tf.StartsWith('net4')) + { + Copy-Item "artifacts\${{parameters.referenceType }}\bin\Windows_NT\${{parameters.Configuration }}.AnyCPU\Microsoft.Data.SqlClient\netfx\Microsoft.Data.SqlClient.dll" "$software\win\$tf" -recurse + } + else + { + Copy-Item "artifacts\${{parameters.referenceType }}\bin\Windows_NT\${{parameters.Configuration }}.AnyCPU\Microsoft.Data.SqlClient\netcore\$tf\Microsoft.Data.SqlClient.dll" "$software\win\$tf" -recurse + } + + $symbols = '${{parameters.symbolsFolder}}' + md $symbols\win\$tf + + if ($tf.StartsWith('net4')) + { + Copy-Item "artifacts\Project\bin\Windows_NT\Release.AnyCPU\Microsoft.Data.SqlClient\netfx\Microsoft.Data.SqlClient.pdb" "$symbols\win\$tf" -recurse + } + else + { + Copy-Item "artifacts\Project\bin\Windows_NT\Release.AnyCPU\Microsoft.Data.SqlClient\netcore\$tf\Microsoft.Data.SqlClient.pdb" "$symbols\win\$tf" -recurse + } + + Write-Host "Artifacts fetched for testing" + Get-Location + displayName: 'Prepare ${{ targetFramework }} Arifacts for Testing' + + - ${{ if eq(parameters.product, 'AKV') }}: + - powershell: | + $software = '${{parameters.softwareFolder}}' + $tf = '${{ targetFramework }}' + md $software\win\$tf + + Copy-Item "artifacts\${{parameters.referenceType }}\bin\Windows_NT\${{parameters.Configuration }}.AnyCPU\AzureKeyVaultProvider\$tf\Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.dll" "$software\win\$tf" -recurse + + $symbols = '${{parameters.symbolsFolder}}' + md $symbols\win\$tf + + Copy-Item "artifacts\${{parameters.referenceType }}\bin\Windows_NT\${{parameters.Configuration }}.AnyCPU\AzureKeyVaultProvider\$tf\Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.pdb" "$symbols\win\$tf" -recurse + + Write-Host "Artifacts fetched for testing" + Get-Location + displayName: 'Prepare ${{ targetFramework }} Arifacts for Testing' + +- powershell: | + $software = '${{parameters.softwareFolder}}' + $symbols = '${{parameters.symbolsFolder}}' + + Get-ChildItem -recurse "$software\*.dll" + Get-ChildItem -recurse "$symbols\*.pdb" + displayName: 'List the prepared files' diff --git a/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml b/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml new file mode 100644 index 0000000000..d639eac044 --- /dev/null +++ b/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml @@ -0,0 +1,106 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: artifactType + values: + - dll + - pkg + + - name: sourceRoot + type: string + default: $(REPOROOT) + + - name: artifactDirectory + type: string + default: $(artifactDirectory) + +steps: +- ${{ if eq(parameters.artifactType, 'dll') }}: + - task: SFP.build-tasks.custom-build-task-2.EsrpMalwareScanning@4 + displayName: 'ESRP MalwareScanning' + inputs: + ConnectedServiceName: 'SqlClient ESRP Malware Scanning' + FolderPath: '${{parameters.sourceRoot }}' + Pattern: '*.dll' + Region: US + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@4 + displayName: 'ESRP CodeSigning' + inputs: + ConnectedServiceName: 'SqlClient ESRP Code Signing' + FolderPath: '${{parameters.sourceRoot }}' + Pattern: '*.dll' + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "Microsoft Data SqlClient Data Provider for SQL Server" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "http://www.microsoft.com" + }, + { + "parameterName": "FileDigest", + "parameterValue": "/fd \"SHA256\"" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolVerify", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + +- ${{ if eq(parameters.artifactType, 'pkg') }}: + - task: SFP.build-tasks.custom-build-task-2.EsrpMalwareScanning@4 + displayName: 'ESRP MalwareScanning Nuget Package' + inputs: + ConnectedServiceName: 'SqlClient ESRP Malware Scanning' + FolderPath: '${{parameters.artifactDirectory }}' + Pattern: '*.nupkg' + Region: US + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@4 + displayName: 'ESRP CodeSigning Nuget Package' + inputs: + ConnectedServiceName: 'SqlClient ESRP Code Signing' + FolderPath: '${{parameters.artifactDirectory }}' + Pattern: '*.nupkg' + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-401405", + "operationSetCode": "NuGetSign", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-401405", + "operationSetCode": "NuGetVerify", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] diff --git a/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml b/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml new file mode 100644 index 0000000000..34e3544ce6 --- /dev/null +++ b/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml @@ -0,0 +1,42 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# +parameters: + - name: nuspecPath + type: string + default: '$(nuspecPath)' + + - name: NugetPackageVersion + type: string + default: '$(NugetPackageVersion)' + + - name: OutputDirectory + type: string + default: '$(Build.SourcesDirectory)/packages' + + - name: Configuration + type: string + default: '$(Configuration)' + + - name: referenceType + default: project + values: + - project + - package + +steps: +- task: NuGetToolInstaller@1 + displayName: 'Install Latest Nuget' + inputs: + checkLatest: true +- powershell: | + $Commit=git rev-parse HEAD + Write-Host "##vso[task.setvariable variable=CommitHead;]$Commit" + displayName: CommitHead +- task: NuGetCommand@2 + displayName: 'NuGet pack' + inputs: + command: custom + arguments: 'pack ${{parameters.nuspecPath}} -Version ${{parameters.NugetPackageVersion}} -OutputDirectory ${{parameters.OutputDirectory}} -properties "COMMITID=$(CommitHead);Configuration=${{parameters.Configuration}};ReferenceType=${{parameters.referenceType}}"' diff --git a/eng/pipelines/common/templates/steps/pre-build-step.yml b/eng/pipelines/common/templates/steps/pre-build-step.yml new file mode 100644 index 0000000000..327b5f21a5 --- /dev/null +++ b/eng/pipelines/common/templates/steps/pre-build-step.yml @@ -0,0 +1,20 @@ +################################################################################# +# 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. # +################################################################################# +steps: +- script: SET + displayName: 'Print Environment Variables' + +- powershell: | + # use sqlcmd to try to connect to localdb + $svc_name = "SQLBrowser" + Get-Service $svc_name | Select-Object -Property Name, StartType, Status + Set-Service -StartupType Automatic $svc_name + net start $svc_name + Get-Service $svc_name | Select-Object -Property Name, StartType, Status + displayName: 'Start SQLBrowser' + +- task: NuGetToolInstaller@1 + displayName: 'Use NuGet ' diff --git a/eng/pipelines/common/templates/steps/prepare-test-db-step.yml b/eng/pipelines/common/templates/steps/prepare-test-db-step.yml new file mode 100644 index 0000000000..8597a0c9e5 --- /dev/null +++ b/eng/pipelines/common/templates/steps/prepare-test-db-step.yml @@ -0,0 +1,26 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: databaseName + type: string + default: $(Database) + + - name: targetFramework + type: string + default: net6.0 + +steps: +- task: DotNetCoreCLI@2 + displayName: 'Build Ext Utilities' + inputs: + arguments: '-f ${{parameters.targetFramework }}' + workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities +- task: DotNetCoreCLI@2 + displayName: 'Create Test Database' + inputs: + command: run + arguments: '-f ${{parameters.targetFramework }} -- "CreateDatabase" ${{parameters.databaseName }} ' + workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities diff --git a/eng/pipelines/common/templates/steps/publish-symbols-step.yml b/eng/pipelines/common/templates/steps/publish-symbols-step.yml new file mode 100644 index 0000000000..5898fddd4a --- /dev/null +++ b/eng/pipelines/common/templates/steps/publish-symbols-step.yml @@ -0,0 +1,64 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: SymAccount + type: string + + - name: publishSymbols + type: string + default: '$(PublishSymbols)' + + - name: symbolsVersion + type: string + default: '$(NuGetPackageVersion)' + + - name: referenceType + default: project + values: + - project + - package + + - name: product + default: MDS + values: + - MDS + - AKV + - MSS + +steps: +- powershell: 'Write-Host "##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{parameters.SymAccount}}"' + displayName: 'Update Symbol.AccountName ${{parameters.SymAccount}}' + condition: and(succeeded(), ${{ eq(parameters.publishSymbols, 'true') }}) + +- ${{ if eq(parameters.product, 'MDS') }}: + - task: PublishSymbols@2 + displayName: 'Publish symbols path' + inputs: + SymbolsFolder: '$(Build.SourcesDirectory)\artifacts\${{parameters.referenceType }}\bin' + SearchPattern: | + Windows_NT/$(Configuration).AnyCPU/**/Microsoft.Data.SqlClient.pdb + Unix/$(Configuration).AnyCPU/**/Microsoft.Data.SqlClient.pdb + IndexSources: false + SymbolServerType: TeamServices + SymbolsMaximumWaitTime: 60 + SymbolsProduct: Microsoft.Data.SqlClient + SymbolsVersion: '{{parameters.symbolsVersion }}' + condition: and(succeeded(), ${{ eq(parameters.publishSymbols, 'true') }}) + +- ${{ if eq(parameters.product, 'AKV') }}: + - task: PublishSymbols@2 + displayName: 'Publish symbols path' + inputs: + SymbolsFolder: '$(Build.SourcesDirectory)\artifacts\${{parameters.referenceType }}\bin' + SearchPattern: | + Windows_NT/$(Configuration).AnyCPU/AzureKeyVaultProvider/**/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.pdb + AnyOS/$(Configuration).AnyCPU/AzureKeyVaultProvider/**/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.pdb + IndexSources: false + SymbolServerType: TeamServices + SymbolsMaximumWaitTime: 60 + SymbolsProduct: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider + SymbolsVersion: '{{parameters.symbolsVersion }}' + condition: and(succeeded(), ${{ eq(parameters.publishSymbols, 'true') }}) diff --git a/eng/pipelines/common/templates/steps/update-config-file-step.yml b/eng/pipelines/common/templates/steps/update-config-file-step.yml new file mode 100644 index 0000000000..2530f35d23 --- /dev/null +++ b/eng/pipelines/common/templates/steps/update-config-file-step.yml @@ -0,0 +1,35 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: TCPConnectionString + type: string + default: '' + + - name: NPConnectionString + type: string + default: '' + + - name: SupportsIntegratedSecurity + type: boolean + default: false + +steps: +# All properties should be added here, and this template should be used for any manipulation of the config.json file. +- powershell: | + $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json + foreach ($p in $jdata) + { + if ("${{parameters.TCPConnectionString }}" -ne ""){ + $p.TCPConnectionString="${{parameters.TCPConnectionString }}"} + + if ("${{parameters.NPConnectionString }}" -ne ""){ + $p.NPConnectionString="${{parameters.NPConnectionString }}"} + + $p.SupportsIntegratedSecurity=[System.Convert]::ToBoolean("${{parameters.SupportsIntegratedSecurity }}") + } + $jdata | ConvertTo-Json | Set-Content "config.json" + workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities + displayName: 'Update config.json' diff --git a/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml b/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml new file mode 100644 index 0000000000..61bd394af4 --- /dev/null +++ b/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml @@ -0,0 +1,78 @@ +################################################################################# +# 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. # +################################################################################# +parameters: + - name: downloadedNugetPath # path to the downloaded nuget files + type: string + + - name: nugetPackageVersion + type: string + default: $(NugetPackageVersion) + +steps: +- powershell: | + # Get a list of package sources available + Get-PackageSource + + #Current location + Get-Location + + # Register the local nuget folder to be used by nuget.config + Register-PackageSource -Name "Package Source" -Location ${{parameters.downloadedNugetPath }} -Force -ProviderName NuGet -Trusted + + # Get a list of package sources available after the change + Get-PackageSource + + #Set the Nuget.config file in the project to use extracted package + $rootFolder = Get-location + [Xml] $nugetConfig = Get-Content -Path "src\Nuget.config" + $Value = Resolve-Path ${{parameters.downloadedNugetPath }} + $newAdd = $nugetConfig.CreateElement("add") + $newAdd.SetAttribute("key","Package source") + $newAdd.SetAttribute("value", "$Value\" ) + $nugetConfig.configuration.packageSources.AppendChild($newAdd) + $nugetConfig.Save("$rootFolder\src\Nuget.config") + displayName: 'Update NuGet config file to read from Nuget folder' + +- task: MSBuild@1 + displayName: 'Restore nugets' + inputs: + solution: build.proj + msbuildArchitecture: x64 + msbuildArguments: '-t:restore' + +- powershell: | + $Doc = [xml](Get-Content ".\Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj") + $parent_xpath = '/Project/ItemGroup/ProjectReference' + $node = $Doc.SelectSingleNode($parent_xpath) + $parentNode = $node.ParentNode + while($node -ne $null) { + $node.ParentNode.RemoveChild($node) + $node = $Doc.SelectSingleNode($parent_xpath) + } + + $parent_xpath = '/Project/ItemGroup/PackageReference[@Include="Microsoft.Data.SqlClient"]' + $node = $Doc.SelectSingleNode($parent_xpath) + + if($node -ne $null){ + $node.Version="${{parameters.nugetPackageVersion }}" + } + else{ + $packagerefnode = $doc.createelement("packagereference") + $value = $doc.selectsinglenode('/project/itemgroup/projectreference') + $attrinclude = $doc.createattribute("include") + $attrinclude.value = "microsoft.data.sqlclient" + $attrversion = $doc.createattribute("version") + $attrversion.value = "${{parameters.nugetPackageVersion }}" + $packagerefnode.attributes.append($attrinclude) + $packagerefnode.attributes.append($attrversion) + $parentNode.AppendChild($packageRefNode) + } + + $currentFolder = Get-Location + $filePath = Join-Path $currentFolder "Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj" + $Doc.Save($filePath) + workingDirectory: 'src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider' + displayName: 'Update AKV Project Ref to Package Ref (.NET Framework/Core)' diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml new file mode 100644 index 0000000000..6392a30428 --- /dev/null +++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml @@ -0,0 +1,159 @@ +################################################################################# +# 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. # +################################################################################# + +name: $(Year:YY)$(DayOfYear)$(Rev:.rr) +trigger: + branches: + include: + - internal/release/5.1 + paths: + include: + - src + - eng + - tools + - .config + - build.proj + - '*.cmd' + - '*.sh' + +schedules: +- cron: '30 23 * * Sun' + displayName: Weekly Sunday 4:30 PM (UTC - 7) Build + branches: + include: + - internal/release/5.1 + always: true + +- cron: '30 3 * * Mon-Fri' + displayName: Mon-Fri 8:30 PM (UTC - 7) Build + branches: + include: + - internal/release/5.1 + +parameters: # parameters are shown up in ADO UI in a build queue time +- name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false +- name: oneBranchType + displayName: 'Select OneBranch template' + default: Official + values: + - NonOfficial + - Official +- name: publishSymbols + type: boolean + default: false +- name: MDS_PackageRef_Version + displayName: 'MDS package version of AKV Provider (build AKV)' + type: string + default: 3.0.0 +- name: CurrentNetFxVersion + displayName: 'Lowest supported .NET Framework version (MDS validation)' + type: string + default: 'net462' + +variables: + - template: /eng/pipelines/libraries/variables.yml@self + - name: packageFolderName + value: drop_buildMDS_build_signed_package + - name: PublishSymbols + value: ${{ parameters['publishSymbols'] }} + - name: MDS_PackageRef_Version + value: ${{ parameters['MDS_PackageRef_Version'] }} + - name: CurrentNetFxVersion + value: ${{ parameters['CurrentNetFxVersion'] }} + - name: ProductVersion #MDS product version (MDS validation) + value: $(NUGETPACKAGEVERSION) + +resources: + repositories: + - repository: templates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + template: v2/OneBranch.${{ parameters['oneBranchType'] }}.CrossPlat.yml@templates # https://aka.ms/obpipelines/templates + parameters: + featureFlags: + WindowsHostVersion: 1ESWindows2022 + globalSdl: # https://aka.ms/obpipelines/sdl + apiscan: + enabled: true + softwareFolder: $(softwareFolder) + symbolsFolder: $(symbolsFolder) + softwarename: Microsoft.Data.SqlClient + versionNumber: $(AssemblyFileVersion) + tsa: + enabled: true # onebranch publish all sdl results to TSA. If TSA is disabled all SDL tools will forced into 'break' build mode. + codeql: + compiled: + enabled: false #[warning]Consider running CodeQL on the default branch only. + sbom: + enabled: true + packageName: Microsoft.Data.SqlClient + packageVersion: $(NugetPackageVersion) + asyncSdl: + enabled: false + credscan: + enabled: true + suppressionsFile: $(REPOROOT)/.config/CredScanSuppressions.json + binskim: + enabled: true + policheck: + enabled: true + break: true # always break the build on policheck issues. You can disable it by setting to 'false' + exclusionsFile: $(REPOROOT)\.config\PolicheckExclusions.xml + armory: + enabled: true + break: true + eslint: # TypeScript and JavaScript + enabled: false + roslyn: + enabled: true + break: true + publishLogs: + enabled: true + tsaOptionsPath: $(REPOROOT)\.config\tsaoptions.json + disableLegacyManifest: true + stages: + - stage: buildAKV + jobs: + - template: eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml@self + parameters: + symbolsFolder: $(symbolsFolder) + softwareFolder: $(softwareFolder) + publishSymbols: ${{ parameters['publishSymbols'] }} + + - stage: buildMDS + jobs: + - template: eng/pipelines/common/templates/jobs/build-signed-package-job.yml@self + parameters: + symbolsFolder: $(symbolsFolder) + softwareFolder: $(softwareFolder) + publishSymbols: ${{ parameters['publishSymbols'] }} + + - stage: package_validation + dependsOn: buildMDS + jobs: + - template: eng/pipelines/common/templates/jobs/validate-signed-package-job.yml@self + parameters: + packageFolderName: $(packageFolderName) + downloadPackageStep: + download: current + artifact: $(packageFolderName) + patterns: '**/*.nupkg' + displayName: 'Download NuGet Package' + + - template: eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml@self + parameters: + packageFolderName: $(packageFolderName) + downloadPackageStep: + download: current + artifact: $(packageFolderName) + patterns: '**/*.nupkg' + displayName: 'Download NuGet Package' diff --git a/eng/pipelines/libraries/akv-variables.yml b/eng/pipelines/libraries/akv-variables.yml new file mode 100644 index 0000000000..a4aa80d555 --- /dev/null +++ b/eng/pipelines/libraries/akv-variables.yml @@ -0,0 +1,21 @@ +################################################################################# +# 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. # +################################################################################# + +variables: + - group: AKV Release Variables + - name: AKVMajor + value: 5 + - name: AKVMinor + value: 1 + - name: AKVPatch + value: 0 + + - name: AKVNugetPackageVersion + value: $(AKVMajor).$(AKVMinor).$(AKVPatch) + - name: AKVAssemblyFileVersion + value: '$(AKVMajor).$(AKVMinor)$(AKVPatch).$(Build.BuildNumber)' + - name: akvNuspecPath + value: tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec diff --git a/eng/pipelines/libraries/build-variables.yml b/eng/pipelines/libraries/build-variables.yml new file mode 100644 index 0000000000..1c26f26a69 --- /dev/null +++ b/eng/pipelines/libraries/build-variables.yml @@ -0,0 +1,10 @@ +################################################################################# +# 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. # +################################################################################# + +variables: + - template: common-variables.yml@self + - template: akv-variables.yml@self + - template: mds-variables.yml@self diff --git a/eng/pipelines/libraries/common-variables.yml b/eng/pipelines/libraries/common-variables.yml new file mode 100644 index 0000000000..fb85f70d36 --- /dev/null +++ b/eng/pipelines/libraries/common-variables.yml @@ -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. # +################################################################################# + +variables: + - name: Configuration + value: Release + - name: CommitHead + value: '' # the value will be extracted from the repo's head + - name: REPOROOT + value: $(Build.SourcesDirectory) + - name: softwareFolder + value: $(REPOROOT)/software + - name: symbolsFolder + value: $(REPOROOT)/symbols + - name: artifactDirectory + value: '$(REPOROOT)/packages' diff --git a/eng/pipelines/libraries/mds-validation-variables.yml b/eng/pipelines/libraries/mds-validation-variables.yml new file mode 100644 index 0000000000..68bf6cad01 --- /dev/null +++ b/eng/pipelines/libraries/mds-validation-variables.yml @@ -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. # +################################################################################# + +variables: + - template: common-variables.yml@self + - template: mds-variables.yml@self + + - name: TempFolderName # extract the nuget package here + value: temp + - name: extractedNugetPath + value: $(Build.SourcesDirectory)\$(TempFolderName)\Microsoft.Data.SqlClient.$(NugetPackageVersion) + - name: expectedFolderNames + value: lib,ref,runtimes + - name: expectedDotnetVersions + value: net462,net6.0,netstandard2.0,netstandard2.1 + - name: Database + value: Northwind + - name: platform + value: AnyCPU + - name: TargetNetFxVersion + value: net48 + - name: TargetNetCoreVersion + value: net6.0 + - name: SQLTarget + value: localhost + - name: encrypt + value: false + - name: SQL_NP_CONN_STRING + value: Data Source=np:$(SQLTarget);Initial Catalog=$(Database);Integrated Security=true;Encrypt=$(ENCRYPT);TrustServerCertificate=true; + - name: SQL_TCP_CONN_STRING + value: Data Source=tcp:$(SQLTarget);Initial Catalog=$(Database);Integrated Security=true;Encrypt=$(ENCRYPT);TrustServerCertificate=true; diff --git a/eng/pipelines/libraries/mds-variables.yml b/eng/pipelines/libraries/mds-variables.yml new file mode 100644 index 0000000000..4dad2e262c --- /dev/null +++ b/eng/pipelines/libraries/mds-variables.yml @@ -0,0 +1,24 @@ +################################################################################# +# 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. # +################################################################################# + +variables: + - group: Release Variables + + - name: Major + value: 5 + - name: Minor + value: 1 + - name: Patch + value: 6 + - name: Packaging.EnableSBOMSigning + value: true + + - name: NugetPackageVersion + value: $(Major).$(Minor).$(Patch) + - name: AssemblyFileVersion + value: '$(Major).$(Minor)$(Patch).$(Build.BuildNumber)' + - name: nuspecPath + value: '$(REPOROOT)/tools/specs/Microsoft.Data.SqlClient.nuspec' diff --git a/eng/pipelines/libraries/variables.yml b/eng/pipelines/libraries/variables.yml new file mode 100644 index 0000000000..57894459d3 --- /dev/null +++ b/eng/pipelines/libraries/variables.yml @@ -0,0 +1,17 @@ +################################################################################# +# 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. # +################################################################################# + +variables: + - template: build-variables.yml@self + # onebranch template variables + - name: ob_outputDirectory + value: '$(artifactDirectory)' # this directory is uploaded to pipeline artifacts, reddog and cloudvault. More info at https://aka.ms/obpipelines/artifacts + - name: ob_sdl_binskim_break + value: true # https://aka.ms/obpipelines/sdl + - name: Packaging.EnableSBOMSigning + value: true + - name: WindowsContainerImage + value: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' # Docker image which is used to build the project https://aka.ms/obpipelines/containers 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 85f4588c8c..e9b86901f4 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 @@ -758,13 +758,14 @@ private static string ToFriendlyName(this SslProtocols protocol) { name = "TLS 1.0"; } -#pragma warning disable CS0618 // Type or member is obsolete: SSL is depricated +// SSL 2.0 and 3.0 are only referenced to log a warning, not explicitly used for connections +#pragma warning disable CS0618, CA5397 else if ((protocol & SslProtocols.Ssl3) == SslProtocols.Ssl3) { name = "SSL 3.0"; } else if ((protocol & SslProtocols.Ssl2) == SslProtocols.Ssl2) -#pragma warning restore CS0618 // Type or member is obsolete: SSL is depricated +#pragma warning restore CS0618, CA5397 { name = "SSL 2.0"; } @@ -784,9 +785,10 @@ private static string ToFriendlyName(this SslProtocols protocol) public static string GetProtocolWarning(this SslProtocols protocol) { string message = string.Empty; -#pragma warning disable CS0618 // Type or member is obsolete : SSL is depricated +// SSL 2.0 and 3.0 are only referenced to log a warning, not explicitly used for connections +#pragma warning disable CS0618, CA5397 if ((protocol & (SslProtocols.Ssl2 | SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11)) != SslProtocols.None) -#pragma warning restore CS0618 // Type or member is obsolete : SSL is depricated +#pragma warning restore CS0618, CA5397 { message = StringsHelper.Format(Strings.SEC_ProtocolWarning, protocol.ToFriendlyName()); } 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 c17a6f9bd4..bb28f09010 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 @@ -447,13 +447,14 @@ internal override uint WaitForSSLHandShakeToComplete(out int protocolVersion) } else if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_SSL3_CLIENT) || nativeProtocol.HasFlag(NativeProtocols.SP_PROT_SSL3_SERVER)) { -#pragma warning disable CS0618 // Type or member is obsolete : SSL is depricated +// SSL 2.0 and 3.0 are only referenced to log a warning, not explicitly used for connections +#pragma warning disable CS0618, CA5397 protocolVersion = (int)SslProtocols.Ssl3; } else if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_SSL2_CLIENT) || nativeProtocol.HasFlag(NativeProtocols.SP_PROT_SSL2_SERVER)) { protocolVersion = (int)SslProtocols.Ssl2; -#pragma warning restore CS0618 // Type or member is obsolete : SSL is depricated +#pragma warning restore CS0618, CA5397 } else //if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_NONE)) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index e536017afb..1f2e758cef 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -243,8 +243,14 @@ private bool VerifyHealthReportAgainstRootCertificate(X509Certificate2Collection chain.ChainPolicy.ExtraStore.Add(cert); } + // An Always Encrypted-enabled driver doesn't verify an expiration date or a certificate authority chain. + // A certificate is simply used as a key pair consisting of a public and private key. This is by design. + + #pragma warning disable IA5352 + // CodeQL [SM00395] By design. Always Encrypted certificates should not be checked. chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - + #pragma warning restore IA5352 + if (!chain.Build(healthReportCert)) { bool untrustedRoot = false; 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 e243d20e26..d85d19ea61 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -55,32 +55,32 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti - - - - - + + + + + - - - + + + - - - + + + - + - - - + + + - - - + + + - + From 0de9681fa0dc06ecf4e524e20e17c654b56c4528 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Fri, 17 May 2024 10:56:12 -0700 Subject: [PATCH 28/76] [5.1.6] | Test | Remove AAS - VBS tests from test pipelines (#2474) (#2509) --- BUILDGUIDE.md | 1 - .../tests/ManualTests/DataCommon/DataTestUtility.cs | 7 ------- .../Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs | 8 -------- .../Microsoft.Data.SqlClient.TestUtilities/Config.cs | 1 - .../config.default.json | 1 - 5 files changed, 18 deletions(-) diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index e5f39c1cd9..8b2c950997 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -165,7 +165,6 @@ Manual Tests require the below setup to run: |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};`| - |TCPConnectionStringAASVBS | (Optional) Connection String for a TCP enabled SQL Server with a VBS Enclave and using Microsoft Azure Attestation (AAS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = AAS; Enclave Attestation Url = {AttestationURL};`| |TCPConnectionStringNoneVBS | (Optional) Connection String for a TCP enabled SQL Server with a VBS Enclave and using None Attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = NONE;`| |TCPConnectionStringAASSGX | (Optional) Connection String for a TCP enabled SQL Server with a SGX Enclave and using Microsoft Azure Attestation (AAS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = AAS; Enclave Attestation Url = {AttestationURL};`| |EnclaveEnabled | Enables tests requiring an enclave-configured server.| diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 3b09804a82..1370d3f1bd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -31,7 +31,6 @@ public static class DataTestUtility public static readonly string NPConnectionString = null; public static readonly string TCPConnectionString = null; public static readonly string TCPConnectionStringHGSVBS = null; - public static readonly string TCPConnectionStringAASVBS = null; public static readonly string TCPConnectionStringNoneVBS = null; public static readonly string TCPConnectionStringAASSGX = null; public static readonly string AADAuthorityURL = null; @@ -90,7 +89,6 @@ static DataTestUtility() NPConnectionString = c.NPConnectionString; TCPConnectionString = c.TCPConnectionString; TCPConnectionStringHGSVBS = c.TCPConnectionStringHGSVBS; - TCPConnectionStringAASVBS = c.TCPConnectionStringAASVBS; TCPConnectionStringNoneVBS = c.TCPConnectionStringNoneVBS; TCPConnectionStringAASSGX = c.TCPConnectionStringAASSGX; AADAuthorityURL = c.AADAuthorityURL; @@ -149,11 +147,6 @@ static DataTestUtility() AEConnStringsSetup.Add(TCPConnectionStringHGSVBS); } - if (!string.IsNullOrEmpty(TCPConnectionStringAASVBS)) - { - AEConnStrings.Add(TCPConnectionStringAASVBS); - } - if (!string.IsNullOrEmpty(TCPConnectionStringNoneVBS)) { AEConnStrings.Add(TCPConnectionStringNoneVBS); diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs index 6a936ac5a1..52bf5c30f4 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs @@ -24,7 +24,6 @@ public static class SqlDbManager private const string TCPConnectionString = "TCPConnectionString"; private const string NPConnectionString = "NPConnectionString"; private const string TCPConnectionStringAASSGX = "TCPConnectionStringAASSGX"; - private const string TCPConnectionStringAASVBS = "TCPConnectionStringAASVBS"; private const string TCPConnectionStringHGSVBS = "TCPConnectionStringHGSVBS"; /// @@ -124,10 +123,6 @@ private static void LoadActiveConnectionStrings() { s_activeConnectionStrings.Add(TCPConnectionStringAASSGX, s_configJson.TCPConnectionStringAASSGX); } - if (!string.IsNullOrEmpty(s_configJson.TCPConnectionStringAASVBS)) - { - s_activeConnectionStrings.Add(TCPConnectionStringAASVBS, s_configJson.TCPConnectionStringAASVBS); - } if (!string.IsNullOrEmpty(s_configJson.TCPConnectionStringHGSVBS)) { s_activeConnectionStrings.Add(TCPConnectionStringHGSVBS, s_configJson.TCPConnectionStringHGSVBS); @@ -148,9 +143,6 @@ private static void UpdateConfig(string key, SqlConnectionStringBuilder builder) case TCPConnectionStringAASSGX: s_configJson.TCPConnectionStringAASSGX = builder.ConnectionString; break; - case TCPConnectionStringAASVBS: - s_configJson.TCPConnectionStringAASVBS = builder.ConnectionString; - break; case TCPConnectionStringHGSVBS: s_configJson.TCPConnectionStringHGSVBS = builder.ConnectionString; break; 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 5c3ca85072..882232b123 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 @@ -13,7 +13,6 @@ public class Config public string TCPConnectionString = null; public string NPConnectionString = null; public string TCPConnectionStringHGSVBS = null; - public string TCPConnectionStringAASVBS = null; public string TCPConnectionStringNoneVBS = null; public string TCPConnectionStringAASSGX = null; public string AADAuthorityURL = 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 b83ffe8883..7ee2ec5d77 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 @@ -2,7 +2,6 @@ "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true;Encrypt=false;", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;Encrypt=false;", "TCPConnectionStringHGSVBS": "", - "TCPConnectionStringAASVBS": "", "TCPConnectionStringNoneVBS": "", "TCPConnectionStringAASSGX": "", "EnclaveEnabled": false, From d6b8f779286d6e4a0c4e8027d18af6565779483e Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Fri, 17 May 2024 11:55:23 -0700 Subject: [PATCH 29/76] [5.1.6] | Revert the fix transient fault handling issue with OpenAsync (#1983) (#2508) --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +- .../SqlConnectionBasicTests.cs | 84 +------------------ .../TDS.Servers/TransientFaultTDSServer.cs | 1 - 4 files changed, 8 insertions(+), 85 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 1040061b59..660b5934e0 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 @@ -1820,7 +1820,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec } } - _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); if (connectionOptions != null && (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || @@ -1849,7 +1849,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec // does not require GC.KeepAlive(this) because of ReRegisterForFinalize below. // Set future transient fault handling based on connection options - _applyTransientFaultHandling = connectionOptions != null && connectionOptions.ConnectRetryCount > 0; + _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); var tdsInnerConnection = (SqlInternalConnectionTds)InnerConnection; 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 1ab55969a0..04acfcb612 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 @@ -2059,7 +2059,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec bool result = false; - _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); if (connectionOptions != null && (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || @@ -2102,7 +2102,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec } // Set future transient fault handling based on connection options - _applyTransientFaultHandling = connectionOptions != null && connectionOptions.ConnectRetryCount > 0; + _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); return result; } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index 2ebf172786..e3183ee6a0 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -54,26 +54,6 @@ public async Task PreLoginEncryptionExcludedTest() Assert.Contains("The instance of SQL Server you attempted to connect to does not support encryption.", ex.Message, StringComparison.OrdinalIgnoreCase); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] - [InlineData(40613)] - [InlineData(42108)] - [InlineData(42109)] - [PlatformSpecific(TestPlatforms.Windows)] - public async Task TransientFaultTestAsync(uint errorCode) - { - using TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, errorCode); - SqlConnectionStringBuilder builder = new() - { - DataSource = "localhost," + server.Port, - IntegratedSecurity = true, - Encrypt = SqlConnectionEncryptOption.Optional - }; - - using SqlConnection connection = new(builder.ConnectionString); - await connection.OpenAsync(); - Assert.Equal(ConnectionState.Open, connection.State); - } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] [InlineData(40613)] [InlineData(42108)] @@ -97,70 +77,14 @@ public void TransientFaultTest(uint errorCode) } catch (Exception e) { + if (null != connection) + { + Assert.Equal(ConnectionState.Closed, connection.State); + } Assert.False(true, e.Message); } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] - [InlineData(40613)] - [InlineData(42108)] - [InlineData(42109)] - [PlatformSpecific(TestPlatforms.Windows)] - public async Task TransientFaultDisabledTestAsync(uint errorCode) - { - using TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, errorCode); - SqlConnectionStringBuilder builder = new() - { - DataSource = "localhost," + server.Port, - IntegratedSecurity = true, - ConnectRetryCount = 0, - Encrypt = SqlConnectionEncryptOption.Optional - }; - - using SqlConnection connection = new(builder.ConnectionString); - try - { - await connection.OpenAsync(); - Assert.False(true, "Connection should not have opened."); - } - catch (SqlException e) - { - // FATAL Error, should result in closed connection. - Assert.Equal(20, e.Class); - Assert.Equal(ConnectionState.Closed, connection.State); - } - } - - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] - [InlineData(40613)] - [InlineData(42108)] - [InlineData(42109)] - [PlatformSpecific(TestPlatforms.Windows)] - public void TransientFaultDisabledTest(uint errorCode) - { - using TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, errorCode); - SqlConnectionStringBuilder builder = new() - { - DataSource = "localhost," + server.Port, - IntegratedSecurity = true, - ConnectRetryCount = 0, - Encrypt = SqlConnectionEncryptOption.Optional - }; - - using SqlConnection connection = new(builder.ConnectionString); - try - { - connection.Open(); - Assert.False(true, "Connection should not have opened."); - } - catch (SqlException e) - { - // FATAL Error, should result in closed connection. - Assert.Equal(20, e.Class); - Assert.Equal(ConnectionState.Closed, connection.State); - } - } - [Fact] public void SqlConnectionDbProviderFactoryTest() { 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 index eda4de8e2a..419f7e5d24 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs @@ -146,7 +146,6 @@ private void Dispose(bool isDisposing) if (isDisposing) { _endpoint?.Stop(); - RequestCounter = 0; } } } From a61ec2e64cd39efa80caa8811fa516be49fc480d Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Thu, 23 May 2024 16:54:52 -0700 Subject: [PATCH 30/76] [5.1.6] Updating Azure.Identity version to 1.11.3 (#2529) --- tools/props/Versions.props | 6 +++--- tools/specs/Microsoft.Data.SqlClient.nuspec | 16 ++++++++-------- ....AlwaysEncrypted.AzureKeyVaultProvider.nuspec | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index bdb5d55136..92ccb38a71 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -27,8 +27,8 @@ - 1.10.3 - 4.56.0 + 1.11.3 + 4.60.3 6.35.0 6.35.0 4.5.1 @@ -55,7 +55,7 @@ - [1.35.0,2.0.0) + [1.38.0,2.0.0) [4.4.0,5.0.0) 6.0.1 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index f7e364e405..1aafd0c799 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -29,8 +29,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -40,8 +40,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -55,8 +55,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -72,8 +72,8 @@ 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 e243d20e26..79140178af 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -26,21 +26,21 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti - + - + - + From 8f2c6cac382cff903cd96dfd166f9c6562a99ab4 Mon Sep 17 00:00:00 2001 From: Daniel Au Date: Fri, 31 May 2024 22:36:01 +0000 Subject: [PATCH 31/76] Merged PR 4622: eng | Fix policheck 5.1 --- eng/pipelines/dotnet-sqlclient-signing-pipeline.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml index 6392a30428..3048e60de5 100644 --- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml @@ -97,6 +97,10 @@ extends: enabled: true packageName: Microsoft.Data.SqlClient packageVersion: $(NugetPackageVersion) + policheck: + enabled: true + break: true # always break the build on policheck issues. You can disable it by setting to 'false' + exclusionsFile: $(REPOROOT)\.config\PolicheckExclusions.xml asyncSdl: enabled: false credscan: @@ -104,10 +108,6 @@ extends: suppressionsFile: $(REPOROOT)/.config/CredScanSuppressions.json binskim: enabled: true - policheck: - enabled: true - break: true # always break the build on policheck issues. You can disable it by setting to 'false' - exclusionsFile: $(REPOROOT)\.config\PolicheckExclusions.xml armory: enabled: true break: true From d14100667a736965687ee8ff870970e779d34499 Mon Sep 17 00:00:00 2001 From: Davoud Eshtehari Date: Fri, 21 Jun 2024 23:48:49 +0000 Subject: [PATCH 32/76] Merged PR 4675: eng | [5.1] Upgrade ESRP to v5 --- .../steps/esrp-code-signing-step.yml | 49 +++++++++++++++---- eng/pipelines/libraries/common-variables.yml | 4 ++ 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml b/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml index d639eac044..67e21ed7ff 100644 --- a/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml +++ b/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml @@ -17,19 +17,37 @@ parameters: type: string default: $(artifactDirectory) + - name: appRegistrationClientId + type: string + default: $(appRegistrationClientId) + + - name: appRegistrationTenantId + type: string + default: $(appRegistrationTenantId) + steps: - ${{ if eq(parameters.artifactType, 'dll') }}: - - task: SFP.build-tasks.custom-build-task-2.EsrpMalwareScanning@4 + - task: EsrpMalwareScanning@5 displayName: 'ESRP MalwareScanning' inputs: - ConnectedServiceName: 'SqlClient ESRP Malware Scanning' + ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' + AuthAKVName: SqlClientDrivers + AuthCertName: 'ESRP-Release-Auth' FolderPath: '${{parameters.sourceRoot }}' Pattern: '*.dll' - Region: US - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@4 + CleanupTempStorage: 1 + VerboseLogin: 1 + - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning' inputs: - ConnectedServiceName: 'SqlClient ESRP Code Signing' + ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' + AuthAKVName: SqlClientDrivers + AuthCertName: 'ESRP-Release-Auth' + AuthSignCertName: 'ESRP-Release-Sign2' FolderPath: '${{parameters.sourceRoot }}' Pattern: '*.dll' signConfigType: inlineSignParams @@ -73,17 +91,28 @@ steps: ] - ${{ if eq(parameters.artifactType, 'pkg') }}: - - task: SFP.build-tasks.custom-build-task-2.EsrpMalwareScanning@4 + - task: EsrpMalwareScanning@5 displayName: 'ESRP MalwareScanning Nuget Package' inputs: - ConnectedServiceName: 'SqlClient ESRP Malware Scanning' + ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' + AuthAKVName: SqlClientDrivers + AuthCertName: 'ESRP-Release-Auth' FolderPath: '${{parameters.artifactDirectory }}' Pattern: '*.nupkg' - Region: US - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@4 + CleanupTempStorage: 1 + VerboseLogin: 1 + - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning Nuget Package' inputs: - ConnectedServiceName: 'SqlClient ESRP Code Signing' + inputs: + ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' + AuthAKVName: SqlClientDrivers + AuthCertName: 'ESRP-Release-Auth' + AuthSignCertName: 'ESRP-Release-Sign2' FolderPath: '${{parameters.artifactDirectory }}' Pattern: '*.nupkg' signConfigType: inlineSignParams diff --git a/eng/pipelines/libraries/common-variables.yml b/eng/pipelines/libraries/common-variables.yml index fb85f70d36..7b2bc00cb1 100644 --- a/eng/pipelines/libraries/common-variables.yml +++ b/eng/pipelines/libraries/common-variables.yml @@ -17,3 +17,7 @@ variables: value: $(REPOROOT)/symbols - name: artifactDirectory value: '$(REPOROOT)/packages' + - name: appRegistrationClientId + value: 'a0d18a38-fde1-4ba7-92e1-15be16cb6a8e' + - name: appRegistrationTenantId + value: '72f988bf-86f1-41af-91ab-2d7cd011db47' From 5645b14a876c8b7ed48c5804c9be77c8bb5109ea Mon Sep 17 00:00:00 2001 From: dauinsight <145612907+dauinsight@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:37:51 -0700 Subject: [PATCH 33/76] Test fixes to accommodate recent infra changes (#2706) Co-authored-by: David Engel --- .../ActiveDirectoryAuthenticationProvider.cs | 6 +++- .../ManualTests/DataCommon/DataTestUtility.cs | 14 ++++++-- .../SQL/AdapterTest/AdapterTest.cs | 9 ++++- .../ConnectivityTests/AADConnectionTest.cs | 15 ++++---- .../SQL/ConnectivityTests/ConnectivityTest.cs | 3 +- .../ConnectionSchemaTest.cs | 36 +++++++++++-------- .../SQL/ExceptionTest/ExceptionTest.cs | 4 +-- .../SqlConfigurationManagerReliabilityTest.cs | 6 ++-- .../SqlConnectionReliabilityTest.cs | 12 ++++--- 9 files changed, 70 insertions(+), 35 deletions(-) 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 6e57bb6c07..76c81e282e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -120,7 +120,11 @@ public override async Task AcquireTokenAsync(SqlAuthenti 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 + int timeout = parameters.ConnectionTimeout * 1000; + if (timeout > 0) // if ConnectionTimeout is 0 or the millis overflows an int, no need to set CancelAfter + { + cts.CancelAfter(timeout); // Convert to milliseconds + } string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix; string[] scopes = new string[] { scope }; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 1370d3f1bd..1375cbea2f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -64,6 +64,7 @@ public static class DataTestUtility public static readonly string EnclaveAzureDatabaseConnString = null; public static bool ManagedIdentitySupported = true; public static string AADAccessToken = null; + public static bool SupportsSystemAssignedManagedIdentity = false; public static string AADSystemIdentityAccessToken = null; public static string AADUserIdentityAccessToken = null; public const string ApplicationClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38"; @@ -83,6 +84,15 @@ public static class DataTestUtility public static readonly string KerberosDomainUser = null; internal static readonly string KerberosDomainPassword = null; + public static bool TcpConnectionStringDoesNotUseAadAuth + { + get + { + SqlConnectionStringBuilder builder = new (TCPConnectionString); + return builder.Authentication == SqlAuthenticationMethod.SqlPassword || builder.Authentication == SqlAuthenticationMethod.NotSpecified; + } + } + static DataTestUtility() { Config c = Config.Load(); @@ -529,7 +539,7 @@ public static string GetAccessToken() public static string GetSystemIdentityAccessToken() { - if (true == ManagedIdentitySupported && null == AADSystemIdentityAccessToken && IsAADPasswordConnStrSetup()) + if (ManagedIdentitySupported && SupportsSystemAssignedManagedIdentity && null == AADSystemIdentityAccessToken && IsAADPasswordConnStrSetup()) { AADSystemIdentityAccessToken = AADUtility.GetManagedIdentityToken().GetAwaiter().GetResult(); if (AADSystemIdentityAccessToken == null) @@ -542,7 +552,7 @@ public static string GetSystemIdentityAccessToken() public static string GetUserIdentityAccessToken() { - if (true == ManagedIdentitySupported && null == AADUserIdentityAccessToken && IsAADPasswordConnStrSetup()) + if (ManagedIdentitySupported && null == AADUserIdentityAccessToken && IsAADPasswordConnStrSetup()) { // Pass User Assigned Managed Identity Client Id here. AADUserIdentityAccessToken = AADUtility.GetManagedIdentityToken(UserManagedIdentityClientId).GetAwaiter().GetResult(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs index d00a9cf8b1..9d43567cf1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs @@ -991,7 +991,14 @@ public void UpdateOffsetTest() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static bool CanRunSchemaTests() + { + return DataTestUtility.AreConnStringsSetup() && + // Tests switch to master database, which is not guaranteed when using AAD auth + DataTestUtility.TcpConnectionStringDoesNotUseAadAuth; + } + + [ConditionalFact(nameof(CanRunSchemaTests))] public void SelectAllTest() { // Test exceptions 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 70cae84d73..8a50cb8ae1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -72,13 +72,14 @@ private static void ConnectAndDisconnect(string connectionString, SqlCredential private static bool IsAccessTokenSetup() => DataTestUtility.IsAccessTokenSetup(); private static bool IsAADConnStringsSetup() => DataTestUtility.IsAADPasswordConnStrSetup(); private static bool IsManagedIdentitySetup() => DataTestUtility.ManagedIdentitySupported; + private static bool SupportsSystemAssignedManagedIdentity() => DataTestUtility.SupportsSystemAssignedManagedIdentity; [PlatformSpecific(TestPlatforms.Windows)] - [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))] + [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))] public static void KustoDatabaseTest() { // This is a sample Kusto database that can be connected by any AD account. - using SqlConnection connection = new SqlConnection("Data Source=help.kusto.windows.net; Authentication=Active Directory Default;Trust Server Certificate=True;"); + using SqlConnection connection = new SqlConnection($"Data Source=help.kusto.windows.net; Authentication=Active Directory Default;Trust Server Certificate=True;User ID = {DataTestUtility.UserManagedIdentityClientId};"); connection.Open(); Assert.True(connection.State == System.Data.ConnectionState.Open); } @@ -556,7 +557,7 @@ public static void ActiveDirectoryDefaultMustPass() { string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + - "Authentication=ActiveDirectoryDefault;"; + $"Authentication=ActiveDirectoryDefault;User ID={DataTestUtility.UserManagedIdentityClientId};"; // Connection should be established using Managed Identity by default. ConnectAndDisconnect(connStr); @@ -612,7 +613,7 @@ public static void ConnectionSpeed() #region Managed Identity Authentication tests - [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))] + [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))] public static void SystemAssigned_ManagedIdentityTest() { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; @@ -630,7 +631,7 @@ public static void UserAssigned_ManagedIdentityTest() ConnectAndDisconnect(connStr); } - [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))] + [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))] public static void AccessToken_SystemManagedIdentityTest() { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; @@ -658,7 +659,7 @@ public static void AccessToken_UserManagedIdentityTest() } } - [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsManagedIdentitySetup))] + [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))] public static void Azure_SystemManagedIdentityTest() { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; @@ -688,7 +689,7 @@ public static void Azure_UserManagedIdentityTest() } } - [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsAccessTokenSetup), nameof(IsManagedIdentitySetup))] + [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsAccessTokenSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))] public static void Azure_AccessToken_SystemManagedIdentityTest() { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; 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 489a414ce9..7675643382 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -362,7 +362,8 @@ public static void ConnectionStringPersistentInfoTest() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // ConnectionOpenDisableRetry relies on error 4060 for automatic retry, which is not returned when using AAD auth + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.TcpConnectionStringDoesNotUseAadAuth))] public static void ConnectionOpenDisableRetry() { SqlConnectionStringBuilder connectionStringBuilder = new(DataTestUtility.TCPConnectionString) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs index 283ddc5c63..0bc5b29806 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs @@ -10,92 +10,98 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class ConnectionSchemaTest { + public static bool CanRunSchemaTests() + { + return DataTestUtility.AreConnStringsSetup() && + // Tests switch to master database, which is not guaranteed when using AAD auth + DataTestUtility.TcpConnectionStringDoesNotUseAadAuth; + } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetTablesFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.Tables, new string[] { "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME", "TABLE_TYPE" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetProceduresFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.Procedures, new string[] { "ROUTINE_SCHEMA", "ROUTINE_NAME", "ROUTINE_TYPE" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetProcedureParametersFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.ProcedureParameters, new string[] { "PARAMETER_MODE", "PARAMETER_NAME" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetDatabasesFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.Databases, new string[] { "database_name", "dbid", "create_date" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetForeignKeysFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.ForeignKeys, new string[] { "CONSTRAINT_TYPE", "IS_DEFERRABLE", "INITIALLY_DEFERRED" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetIndexesFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.Indexes, new string[] { "index_name", "constraint_name" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetIndexColumnsFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.IndexColumns, new string[] { "index_name", "KeyType", "column_name" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetColumnsFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.Columns, new string[] { "IS_NULLABLE", "COLUMN_DEFAULT" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetAllColumnsFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.AllColumns, new string[] { "IS_NULLABLE", "COLUMN_DEFAULT", "IS_FILESTREAM", "IS_SPARSE", "IS_COLUMN_SET" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetColumnSetColumnsFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.ColumnSetColumns, new string[] { "IS_NULLABLE", "COLUMN_DEFAULT", "IS_FILESTREAM", "IS_SPARSE", "IS_COLUMN_SET" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetUsersFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.Users, new string[] { "uid", "user_name" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetViewsFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.Views, new string[] { "TABLE_NAME", "CHECK_OPTION", "IS_UPDATABLE" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetViewColumnsFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.ViewColumns, new string[] { "VIEW_CATALOG", "VIEW_SCHEMA", "VIEW_NAME" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetUserDefinedTypesFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.UserDefinedTypes, new string[] { "assembly_name", "version_revision", "culture_info" }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(nameof(CanRunSchemaTests))] public static void GetStructuredTypeMembersFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.StructuredTypeMembers, new string[] { "TYPE_CATALOG", "TYPE_SCHEMA", "TYPE_NAME", "MEMBER_NAME", "ORDINAL_POSITION" }); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs index c9dfe33bda..e34b47e262 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs @@ -201,7 +201,7 @@ public static void ExceptionTests() } // Synapse: 110003;Invalid user or password - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] public static void VariousExceptionTests() { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); @@ -219,7 +219,7 @@ public static void VariousExceptionTests() } // Test 1 - B - badBuilder = new SqlConnectionStringBuilder(builder.ConnectionString) { Password = string.Empty, IntegratedSecurity = false }; + badBuilder = new SqlConnectionStringBuilder(builder.ConnectionString) { Password = string.Empty, IntegratedSecurity = false, Authentication = SqlAuthenticationMethod.NotSpecified }; using (var sqlConnection = new SqlConnection(badBuilder.ConnectionString)) { string errorMessage = string.Format(CultureInfo.InvariantCulture, logonFailedErrorMessage, badBuilder.UserID); 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 a5e3f901d4..67fd739f5f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs @@ -23,7 +23,8 @@ public class SqlConfigurationManagerReliabilityTest { InitialCatalog = SqlConnectionReliabilityTest.InvalidInitialCatalog, ConnectTimeout = 1 }.ConnectionString; #region Internal Functions - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // Test relies on error 4060 for automatic retry, which is not returned when using AAD auth + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.TcpConnectionStringDoesNotUseAadAuth))] [InlineData(RetryLogicConfigHelper.RetryMethodName_Fix, RetryLogicConfigHelper.RetryMethodName_Inc)] [InlineData(RetryLogicConfigHelper.RetryMethodName_Inc, RetryLogicConfigHelper.RetryMethodName_Exp)] [InlineData(RetryLogicConfigHelper.RetryMethodName_Exp, RetryLogicConfigHelper.RetryMethodName_Fix)] @@ -129,7 +130,8 @@ public void InvalidRetryMethodName(string methodName) s_commandCRLTest.NoneRetriableExecuteFail(TcpCnnString, cmdProvider); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // Test relies on error 4060 for automatic retry, which is not returned when using AAD auth + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.TcpConnectionStringDoesNotUseAadAuth))] [InlineData("InvalidRetrylogicTypeName")] [InlineData("")] [InlineData(null)] 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 2c42f456c9..a745556929 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs @@ -16,7 +16,8 @@ public class SqlConnectionReliabilityTest private readonly string _cancelErrMsgPattern = RetryLogicTestHelper.s_cancelErrMsgPattern; #region Sync - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public void ConnectionRetryOpenInvalidCatalogFailed(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -33,7 +34,8 @@ public void ConnectionRetryOpenInvalidCatalogFailed(string cnnString, SqlRetryLo } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public void ConnectionCancelRetryOpenInvalidCatalog(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -155,7 +157,8 @@ public void DefaultOpenWithoutRetry(string connectionString, SqlRetryLogicBasePr #endregion #region Async - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public async void ConnectionRetryOpenAsyncInvalidCatalogFailed(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -172,7 +175,8 @@ public async void ConnectionRetryOpenAsyncInvalidCatalogFailed(string cnnString, } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + // Test relies on error 4060 for automatic retry, which is not returned when using AAD auth + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.TcpConnectionStringDoesNotUseAadAuth))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public async void ConnectionCancelRetryOpenAsyncInvalidCatalog(string cnnString, SqlRetryLogicBaseProvider provider) { From f63ae8f32a484a979f00bde767c0fb66c464a3ac Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Wed, 24 Jul 2024 17:50:01 -0700 Subject: [PATCH 34/76] [5.1.6] | CVE | Update Azure.Identity from 1.11.3 to 1.11.4 (#2649) --- tools/props/Versions.props | 4 ++-- tools/specs/Microsoft.Data.SqlClient.nuspec | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 92ccb38a71..f6abe583f4 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -27,8 +27,8 @@ - 1.11.3 - 4.60.3 + 1.11.4 + 4.61.3 6.35.0 6.35.0 4.5.1 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 1aafd0c799..006091a196 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -29,8 +29,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -40,8 +40,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -55,8 +55,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + @@ -72,8 +72,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + From bccd975df99e7cf7cd1a7e1b9e2f297d38f3c7b8 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Tue, 30 Jul 2024 10:05:50 -0700 Subject: [PATCH 35/76] [5.1] eng | Secure symbols publishing (#2719) --- .../jobs/build-signed-akv-package-job.yml | 14 +-- .../jobs/build-signed-package-job.yml | 11 +- .../jobs/run-tests-package-reference-job.yml | 1 + .../jobs/validate-signed-package-job.yml | 1 + .../steps/esrp-code-signing-step.yml | 49 ++++++-- .../templates/steps/publish-symbols-step.yml | 106 ++++++++++++++++-- .../dotnet-sqlclient-signing-pipeline.yml | 46 +++++--- eng/pipelines/libraries/common-variables.yml | 4 + 8 files changed, 176 insertions(+), 56 deletions(-) diff --git a/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml b/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml index 228195aa61..43109253dc 100644 --- a/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml +++ b/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml @@ -17,6 +17,7 @@ parameters: jobs: - job: build_signed_akv_package + displayName: 'Build Signed AKV Provider Package' pool: type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs @@ -61,20 +62,11 @@ jobs: product: AKV referenceType: package - # Publish symbols to private server + # Publish symbols to servers - template: ../steps/publish-symbols-step.yml@self parameters: - SymAccount: $(PrivateSymAccount) - referenceType: package - symbolsVersion: ${{variables.AKVNuGetPackageVersion }} - product: AKV - publishSymbols: ${{ parameters['PublishSymbols'] }} - - # Publish symbols to public server - - template: ../steps/publish-symbols-step.yml@self - parameters: - SymAccount: $(PublicSymAccount) referenceType: package symbolsVersion: ${{variables.AKVNuGetPackageVersion }} product: AKV publishSymbols: ${{ parameters['PublishSymbols'] }} + symbolsArtifactName: akv_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_$(NuGetPackageVersion)_$(System.TimelineId) diff --git a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml index ad4344dd73..f07445c291 100644 --- a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml +++ b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml @@ -17,6 +17,7 @@ parameters: jobs: - job: build_signed_package + displayName: 'Build Signed MDS Package' pool: type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs @@ -49,14 +50,8 @@ jobs: parameters: product: MDS - # Publish symbols to private server + # Publish symbols to servers - template: ../steps/publish-symbols-step.yml@self parameters: - SymAccount: $(PrivateSymAccount) - publishSymbols: ${{ parameters['PublishSymbols'] }} - - # Publish symbols to public server - - template: ../steps/publish-symbols-step.yml@self - parameters: - SymAccount: $(PublicSymAccount) publishSymbols: ${{ parameters['PublishSymbols'] }} + symbolsArtifactName: mds_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_$(NuGetPackageVersion)_$(System.TimelineId) diff --git a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml index f764a55201..a9fcd12720 100644 --- a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml +++ b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml @@ -19,6 +19,7 @@ parameters: jobs: - job: run_tests_package_reference + displayName: 'Run tests with package reference' ${{ if ne(parameters.dependsOn, 'empty')}}: dependsOn: '${{parameters.dependsOn }}' pool: diff --git a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml index 1a7ca1ae21..bd15734e22 100644 --- a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml +++ b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml @@ -39,6 +39,7 @@ parameters: jobs: - job: validate_signed_package + displayName: 'Verify signed package' ${{ if ne(parameters.dependsOn, '')}}: dependsOn: '${{parameters.dependsOn }}' pool: diff --git a/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml b/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml index d639eac044..67e21ed7ff 100644 --- a/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml +++ b/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml @@ -17,19 +17,37 @@ parameters: type: string default: $(artifactDirectory) + - name: appRegistrationClientId + type: string + default: $(appRegistrationClientId) + + - name: appRegistrationTenantId + type: string + default: $(appRegistrationTenantId) + steps: - ${{ if eq(parameters.artifactType, 'dll') }}: - - task: SFP.build-tasks.custom-build-task-2.EsrpMalwareScanning@4 + - task: EsrpMalwareScanning@5 displayName: 'ESRP MalwareScanning' inputs: - ConnectedServiceName: 'SqlClient ESRP Malware Scanning' + ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' + AuthAKVName: SqlClientDrivers + AuthCertName: 'ESRP-Release-Auth' FolderPath: '${{parameters.sourceRoot }}' Pattern: '*.dll' - Region: US - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@4 + CleanupTempStorage: 1 + VerboseLogin: 1 + - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning' inputs: - ConnectedServiceName: 'SqlClient ESRP Code Signing' + ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' + AuthAKVName: SqlClientDrivers + AuthCertName: 'ESRP-Release-Auth' + AuthSignCertName: 'ESRP-Release-Sign2' FolderPath: '${{parameters.sourceRoot }}' Pattern: '*.dll' signConfigType: inlineSignParams @@ -73,17 +91,28 @@ steps: ] - ${{ if eq(parameters.artifactType, 'pkg') }}: - - task: SFP.build-tasks.custom-build-task-2.EsrpMalwareScanning@4 + - task: EsrpMalwareScanning@5 displayName: 'ESRP MalwareScanning Nuget Package' inputs: - ConnectedServiceName: 'SqlClient ESRP Malware Scanning' + ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' + AuthAKVName: SqlClientDrivers + AuthCertName: 'ESRP-Release-Auth' FolderPath: '${{parameters.artifactDirectory }}' Pattern: '*.nupkg' - Region: US - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@4 + CleanupTempStorage: 1 + VerboseLogin: 1 + - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning Nuget Package' inputs: - ConnectedServiceName: 'SqlClient ESRP Code Signing' + inputs: + ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' + AuthAKVName: SqlClientDrivers + AuthCertName: 'ESRP-Release-Auth' + AuthSignCertName: 'ESRP-Release-Sign2' FolderPath: '${{parameters.artifactDirectory }}' Pattern: '*.nupkg' signConfigType: inlineSignParams diff --git a/eng/pipelines/common/templates/steps/publish-symbols-step.yml b/eng/pipelines/common/templates/steps/publish-symbols-step.yml index 5898fddd4a..cd1954ce77 100644 --- a/eng/pipelines/common/templates/steps/publish-symbols-step.yml +++ b/eng/pipelines/common/templates/steps/publish-symbols-step.yml @@ -1,11 +1,14 @@ -################################################################################# -# 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. # -################################################################################# +#################################################################################### +# 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. # +# # +# doc: https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL # +#################################################################################### parameters: - name: SymAccount type: string + default: 'SqlClientDrivers' - name: publishSymbols type: string @@ -15,6 +18,23 @@ parameters: type: string default: '$(NuGetPackageVersion)' + - name: symbolServer + type: string + default: '$(SymbolServer)' + + - name: symbolTokenUri + type: string + default: '$(SymbolTokenUri)' + + - name: symbolsArtifactName + type: string + + - name: publishToServers + type: object + default: + internal: true + public: true + - name: referenceType default: project values: @@ -30,12 +50,12 @@ parameters: steps: - powershell: 'Write-Host "##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{parameters.SymAccount}}"' - displayName: 'Update Symbol.AccountName ${{parameters.SymAccount}}' + displayName: 'Update Symbol.AccountName with ${{parameters.SymAccount}}' condition: and(succeeded(), ${{ eq(parameters.publishSymbols, 'true') }}) - ${{ if eq(parameters.product, 'MDS') }}: - task: PublishSymbols@2 - displayName: 'Publish symbols path' + displayName: 'Upload symbols to ${{parameters.SymAccount }} org' inputs: SymbolsFolder: '$(Build.SourcesDirectory)\artifacts\${{parameters.referenceType }}\bin' SearchPattern: | @@ -44,13 +64,16 @@ steps: IndexSources: false SymbolServerType: TeamServices SymbolsMaximumWaitTime: 60 + SymbolExpirationInDays: 1825 # 5 years SymbolsProduct: Microsoft.Data.SqlClient - SymbolsVersion: '{{parameters.symbolsVersion }}' + SymbolsVersion: ${{parameters.symbolsVersion }} + SymbolsArtifactName: ${{parameters.symbolsArtifactName }} + Pat: $(System.AccessToken) condition: and(succeeded(), ${{ eq(parameters.publishSymbols, 'true') }}) - ${{ if eq(parameters.product, 'AKV') }}: - task: PublishSymbols@2 - displayName: 'Publish symbols path' + displayName: 'Upload symbols to ${{parameters.SymAccount }} org' inputs: SymbolsFolder: '$(Build.SourcesDirectory)\artifacts\${{parameters.referenceType }}\bin' SearchPattern: | @@ -59,6 +82,69 @@ steps: IndexSources: false SymbolServerType: TeamServices SymbolsMaximumWaitTime: 60 + SymbolExpirationInDays: 1825 # 5 years SymbolsProduct: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider - SymbolsVersion: '{{parameters.symbolsVersion }}' + SymbolsVersion: ${{parameters.symbolsVersion }} + SymbolsArtifactName: ${{parameters.symbolsArtifactName }} + Pat: $(System.AccessToken) condition: and(succeeded(), ${{ eq(parameters.publishSymbols, 'true') }}) + +- task: AzureCLI@2 + displayName: 'Publish symbols' + condition: and(succeeded(), ${{ eq(parameters.publishSymbols, 'true') }}) + inputs: + azureSubscription: 'Symbols publishing Workload Identity federation service-ADO.Net' + scriptType: ps + scriptLocation: inlineScript + inlineScript: | + $publishToInternalServer = "${{parameters.publishToServers.internal }}".ToLower() + $publishToPublicServer = "${{parameters.publishToServers.public }}".ToLower() + + echo "Publishing request name: ${{parameters.symbolsArtifactName }}" + echo "Publish to internal server: $publishToInternalServer" + echo "Publish to public server: $publishToPublicServer" + + $symbolServer = "${{parameters.symbolServer }}" + $tokenUri = "${{parameters.symbolTokenUri }}" + # Registered project name in the symbol publishing pipeline: https://portal.microsofticm.com/imp/v3/incidents/incident/520844254/summary + $projectName = "Microsoft.Data.SqlClient.SNI" + + # Get the access token for the symbol publishing service + $symbolPublishingToken = az account get-access-token --resource $tokenUri --query accessToken -o tsv + + echo "> 1.Symbol publishing token acquired." + + echo "Registering the request name ..." + $requestName = "${{parameters.symbolsArtifactName }}" + $requestNameRegistrationBody = "{'requestName': '$requestName'}" + Invoke-RestMethod -Method POST -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" -Body $requestNameRegistrationBody + + echo "> 2.Registration of request name succeeded." + + echo "Publishing the symbols ..." + $publishSymbolsBody = "{'publishToInternalServer': $publishToInternalServer, 'publishToPublicServer': $publishToPublicServer}" + echo "Publishing symbols request body: $publishSymbolsBody" + Invoke-RestMethod -Method POST -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests/$requestName" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" -Body $publishSymbolsBody + + echo "> 3.Request to publish symbols succeeded." + + # The following REST calls are used to check publishing status. + echo "> 4.Checking the status of the request ..." + + Invoke-RestMethod -Method GET -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests/$requestName" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" + + echo "Use below tables to interpret the values of xxxServerStatus and xxxServerResult fields from the response." + + echo "PublishingStatus" + echo "-----------------" + echo "0 NotRequested; The request has not been requested to publish." + echo "1 Submitted; The request is submitted to be published" + echo "2 Processing; The request is still being processed" + echo "3 Completed; The request has been completed processing. It can be failed or successful. Check PublishingResult to get more details" + + echo "PublishingResult" + echo "-----------------" + echo "0 Pending; The request has not completed or has not been requested." + echo "1 Succeeded; The request has published successfully" + echo "2 Failed; The request has failed to publish" + echo "3 Cancelled; The request was cancelled" diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml index 3048e60de5..6e9cfe5483 100644 --- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml @@ -38,24 +38,33 @@ parameters: # parameters are shown up in ADO UI in a build queue time displayName: 'Enable debug output' type: boolean default: false -- name: oneBranchType - displayName: 'Select OneBranch template' - default: Official - values: - - NonOfficial - - Official + - name: publishSymbols type: boolean default: false + - name: MDS_PackageRef_Version displayName: 'MDS package version of AKV Provider (build AKV)' type: string default: 3.0.0 + - name: CurrentNetFxVersion displayName: 'Lowest supported .NET Framework version (MDS validation)' type: string default: 'net462' +- name: enableAllSdlTools + displayName: 'Enable all SDL tools' + type: boolean + default: true + +- name: oneBranchType + displayName: 'Select OneBranch template' + default: Official + values: + - NonOfficial + - Official + variables: - template: /eng/pipelines/libraries/variables.yml@self - name: packageFolderName @@ -77,51 +86,52 @@ resources: ref: refs/heads/main extends: - template: v2/OneBranch.${{ parameters['oneBranchType'] }}.CrossPlat.yml@templates # https://aka.ms/obpipelines/templates + template: v2/OneBranch.${{parameters.oneBranchType }}.CrossPlat.yml@templates # https://aka.ms/obpipelines/templates parameters: featureFlags: WindowsHostVersion: 1ESWindows2022 globalSdl: # https://aka.ms/obpipelines/sdl apiscan: - enabled: true + enabled: ${{parameters.enableAllSdlTools }} softwareFolder: $(softwareFolder) symbolsFolder: $(symbolsFolder) softwarename: Microsoft.Data.SqlClient versionNumber: $(AssemblyFileVersion) tsa: - enabled: true # onebranch publish all sdl results to TSA. If TSA is disabled all SDL tools will forced into 'break' build mode. + enabled: ${{parameters.enableAllSdlTools }} # onebranch publish all sdl results to TSA. If TSA is disabled all SDL tools will forced into 'break' build mode. codeql: compiled: enabled: false #[warning]Consider running CodeQL on the default branch only. sbom: - enabled: true + enabled: ${{parameters.enableAllSdlTools }} packageName: Microsoft.Data.SqlClient packageVersion: $(NugetPackageVersion) policheck: - enabled: true + enabled: ${{parameters.enableAllSdlTools }} break: true # always break the build on policheck issues. You can disable it by setting to 'false' exclusionsFile: $(REPOROOT)\.config\PolicheckExclusions.xml asyncSdl: enabled: false credscan: - enabled: true + enabled: ${{parameters.enableAllSdlTools }} suppressionsFile: $(REPOROOT)/.config/CredScanSuppressions.json binskim: - enabled: true + enabled: ${{parameters.enableAllSdlTools }} armory: - enabled: true + enabled: ${{parameters.enableAllSdlTools }} break: true eslint: # TypeScript and JavaScript enabled: false roslyn: - enabled: true + enabled: ${{parameters.enableAllSdlTools }} break: true publishLogs: - enabled: true + enabled: ${{parameters.enableAllSdlTools }} tsaOptionsPath: $(REPOROOT)\.config\tsaoptions.json disableLegacyManifest: true stages: - stage: buildAKV + displayName: 'Build AKV Provider' jobs: - template: eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml@self parameters: @@ -130,6 +140,7 @@ extends: publishSymbols: ${{ parameters['publishSymbols'] }} - stage: buildMDS + displayName: 'Build MDS' jobs: - template: eng/pipelines/common/templates/jobs/build-signed-package-job.yml@self parameters: @@ -137,7 +148,8 @@ extends: softwareFolder: $(softwareFolder) publishSymbols: ${{ parameters['publishSymbols'] }} - - stage: package_validation + - stage: mds_package_validation + displayName: 'MDS Package Validation' dependsOn: buildMDS jobs: - template: eng/pipelines/common/templates/jobs/validate-signed-package-job.yml@self diff --git a/eng/pipelines/libraries/common-variables.yml b/eng/pipelines/libraries/common-variables.yml index fb85f70d36..7b2bc00cb1 100644 --- a/eng/pipelines/libraries/common-variables.yml +++ b/eng/pipelines/libraries/common-variables.yml @@ -17,3 +17,7 @@ variables: value: $(REPOROOT)/symbols - name: artifactDirectory value: '$(REPOROOT)/packages' + - name: appRegistrationClientId + value: 'a0d18a38-fde1-4ba7-92e1-15be16cb6a8e' + - name: appRegistrationTenantId + value: '72f988bf-86f1-41af-91ab-2d7cd011db47' From 0a7d19240d0429a8b393b682aee5a2bd8721c226 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 31 Jul 2024 10:51:47 -0700 Subject: [PATCH 36/76] [5.1] eng | Address strong naming issues in created nugets (#2698) (#2702) --- .../steps/build-all-configurations-signed-dlls-step.yml | 2 +- .../common/templates/steps/code-analyze-step.yml | 2 +- .../netcore/ref/Microsoft.Data.SqlClient.csproj | 9 +++++++++ .../netcore/src/Microsoft.Data.SqlClient.csproj | 9 +++++++++ .../netfx/ref/Microsoft.Data.SqlClient.csproj | 9 +++++++++ .../netfx/src/Microsoft.Data.SqlClient.csproj | 9 +++++++++ 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml b/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml index 1b8401cbc3..647c0ff35f 100644 --- a/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml +++ b/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml @@ -36,7 +36,7 @@ steps: inputs: solution: '**/build.proj' configuration: '${{parameters.Configuration }}' - msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAllConfigurations -p:GenerateNuget=false -p:SignAssembly=true -p:AssemblyOriginatorKeyFile=$(Agent.TempDirectory)\netfxKeypair.snk' + msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAllConfigurations -p:GenerateNuget=false -p:SignAssembly=true -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk' - ${{ if eq(parameters.product, 'AKV') }}: - task: MSBuild@1 diff --git a/eng/pipelines/common/templates/steps/code-analyze-step.yml b/eng/pipelines/common/templates/steps/code-analyze-step.yml index 92be8eabf6..55111a42fe 100644 --- a/eng/pipelines/common/templates/steps/code-analyze-step.yml +++ b/eng/pipelines/common/templates/steps/code-analyze-step.yml @@ -34,7 +34,7 @@ steps: msBuildVersion: 17.0 msBuildArchitecture: x64 setupCommandlinePicker: vs2022 - msBuildCommandline: 'msbuild ${{parameters.sourceRoot}}\build.proj -p:configuration=Release -p:GenerateNuget=false -p:BuildTools=false' + msBuildCommandline: 'msbuild ${{parameters.sourceRoot}}\build.proj -p:configuration=Release -p:GenerateNuget=false -p:BuildTools=false -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk' - ${{ if eq(parameters.product, 'AKV') }}: - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3 displayName: 'Guardian Dotnet Analyzers ' diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index 41e1263abc..5df3580b25 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -12,6 +12,15 @@ netstandard AnyCPU;x64;x86 + + true + true + $(SigningKeyPath) + $(SigningKeyPath) + + + $(SigningKeyPath) + 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 5e08a713a5..08af099e22 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -19,6 +19,15 @@ true Core $(BaseProduct) + + true + true + $(SigningKeyPath) + $(SigningKeyPath) + + + $(SigningKeyPath) + portable true diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 7a80ce32da..8e70505f57 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -8,6 +8,15 @@ Framework $(BaseProduct) Debug;Release; + + true + true + $(SigningKeyPath) + $(SigningKeyPath) + + + $(SigningKeyPath) + 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 a10a693406..1a8063169c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -59,6 +59,15 @@ True False + + true + true + $(SigningKeyPath) + $(SigningKeyPath) + + + $(SigningKeyPath) + From 6fe8e21e2d3885c908b15ffadc2a1f434f51155b Mon Sep 17 00:00:00 2001 From: Aris Rellegue <134557572+arellegue@users.noreply.github.com> Date: Thu, 1 Aug 2024 18:03:46 +0000 Subject: [PATCH 37/76] [5.1] Test | Fixed some Azure managed identity authentication unit test failures (#2652) (#2738) --- .../SQL/ExceptionTest/ExceptionTest.cs | 21 +++++++++++++------ .../SQL/RetryLogic/RetryLogicTestHelper.cs | 5 +++-- .../SqlConnectionReliabilityTest.cs | 6 +++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs index e34b47e262..ce751c4d29 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs @@ -201,11 +201,17 @@ public static void ExceptionTests() } // Synapse: 110003;Invalid user or password - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void VariousExceptionTests() { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); - + // Strip the password in connection string if Authentication=Active Directory Managed Identity as it can not be used with a Password + if (builder.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity) + { + string[] removeKeys = { "Password", "PWD" }; + string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.TCPConnectionString, removeKeys); + builder = new SqlConnectionStringBuilder(connStr); + } // Test 1 - A SqlConnectionStringBuilder badBuilder = new SqlConnectionStringBuilder(builder.ConnectionString) { DataSource = badServer, ConnectTimeout = 1 }; @@ -219,11 +225,14 @@ public static void VariousExceptionTests() } // Test 1 - B - badBuilder = new SqlConnectionStringBuilder(builder.ConnectionString) { Password = string.Empty, IntegratedSecurity = false, Authentication = SqlAuthenticationMethod.NotSpecified }; - using (var sqlConnection = new SqlConnection(badBuilder.ConnectionString)) + if (DataTestUtility.IsNotAzureServer()) { - string errorMessage = string.Format(CultureInfo.InvariantCulture, logonFailedErrorMessage, badBuilder.UserID); - VerifyConnectionFailure(() => sqlConnection.Open(), errorMessage, (ex) => VerifyException(ex, 1, 18456, 1, 14)); + badBuilder = new SqlConnectionStringBuilder(builder.ConnectionString) { Password = string.Empty, IntegratedSecurity = false }; + using (var sqlConnection = new SqlConnection(badBuilder.ConnectionString)) + { + string errorMessage = string.Format(CultureInfo.InvariantCulture, logonFailedErrorMessage, badBuilder.UserID); + VerifyConnectionFailure(() => sqlConnection.Open(), errorMessage, (ex) => VerifyException(ex, 1, 18456, 1, 14)); + } } } 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 ba83e9b22a..a9eb1b52a2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs @@ -71,7 +71,8 @@ private static readonly HashSet s_defaultTransientErrors 20, 0, -2, // Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. - 207 // invalid column name + 207, // invalid column name + 18456 // Using managed identity in Azure Sql Server throws 18456 for non-existent database instead of 4060. }; internal static readonly string s_exceedErrMsgPattern = SystemDataResourceManager.Instance.SqlRetryLogic_RetryExceeded; @@ -117,7 +118,7 @@ public static IEnumerable GetConnectionAndRetryStrategy(int numberOfRe public static IEnumerable GetConnectionAndRetryStrategyInvalidCatalog(int numberOfRetries) { - return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromSeconds(1), FilterSqlStatements.None, null, 250, false); + return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromSeconds(1), FilterSqlStatements.None, null, 250, true); } public static IEnumerable GetConnectionAndRetryStrategyInvalidCommand(int numberOfRetries) 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 a745556929..c78c060677 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs @@ -17,7 +17,7 @@ public class SqlConnectionReliabilityTest #region Sync // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public void ConnectionRetryOpenInvalidCatalogFailed(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -35,7 +35,7 @@ public void ConnectionRetryOpenInvalidCatalogFailed(string cnnString, SqlRetryLo } // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public void ConnectionCancelRetryOpenInvalidCatalog(string cnnString, SqlRetryLogicBaseProvider provider) { @@ -158,7 +158,7 @@ public void DefaultOpenWithoutRetry(string connectionString, SqlRetryLogicBasePr #region Async // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public async void ConnectionRetryOpenAsyncInvalidCatalogFailed(string cnnString, SqlRetryLogicBaseProvider provider) { From ca8746f7cf356d7f001de3d445fc6761dca886f5 Mon Sep 17 00:00:00 2001 From: dauinsight <145612907+dauinsight@users.noreply.github.com> Date: Fri, 16 Aug 2024 09:26:39 -0700 Subject: [PATCH 38/76] [5.1] Add | Cache TokenCredential objects to take advantage of token caching (#2380) (#2776) * Add | Cache TokenCredential objects to take advantage of token caching (dotnet#2380) --- .../ActiveDirectoryAuthenticationProvider.cs | 234 +++++++++++++++--- 1 file changed, 201 insertions(+), 33 deletions(-) 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 76c81e282e..f2fd1aaceb 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -25,14 +25,16 @@ public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationPro /// The purpose of this cache is to allow re-use of Access Tokens fetched for a user interactively or with any other mode /// to avoid interactive authentication request every-time, within application scope making use of MSAL's userTokenCache. /// - private static ConcurrentDictionary s_pcaMap - = new ConcurrentDictionary(); private static readonly MemoryCache s_accountPwCache = new(nameof(ActiveDirectoryAuthenticationProvider)); + private static readonly ConcurrentDictionary s_pcaMap = new(); + private static readonly ConcurrentDictionary s_tokenCredentialMap = new(); + private static SemaphoreSlim s_pcaMapModifierSemaphore = new(1, 1); + private static SemaphoreSlim s_tokenCredentialMapModifierSemaphore = new(1, 1); private static readonly int s_accountPwCacheTtlInHours = 2; private static readonly string s_nativeClientRedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"; private static readonly string s_defaultScopeSuffix = "/.default"; private readonly string _type = typeof(ActiveDirectoryAuthenticationProvider).Name; - private readonly SqlClientLogger _logger = new SqlClientLogger(); + private readonly SqlClientLogger _logger = new(); private Func _deviceCodeFlowCallback; private ICustomWebUi _customWebUI = null; private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId; @@ -66,6 +68,11 @@ public static void ClearUserTokenCache() { s_pcaMap.Clear(); } + + if (!s_tokenCredentialMap.IsEmpty) + { + s_tokenCredentialMap.Clear(); + } } /// @@ -145,38 +152,27 @@ public override async Task AcquireTokenAsync(SqlAuthenti * More information: https://docs.microsoft.com/azure/active-directory/develop/msal-client-application-configuration **/ - int seperatorIndex = parameters.Authority.LastIndexOf('/'); - string authority = parameters.Authority.Remove(seperatorIndex + 1); - string audience = parameters.Authority.Substring(seperatorIndex + 1); + int separatorIndex = parameters.Authority.LastIndexOf('/'); + string authority = parameters.Authority.Remove(separatorIndex + 1); + string audience = parameters.Authority.Substring(separatorIndex + 1); string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId; if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDefault) { - DefaultAzureCredentialOptions defaultAzureCredentialOptions = new() - { - AuthorityHost = new Uri(authority), - SharedTokenCacheTenantId = audience, - VisualStudioCodeTenantId = audience, - VisualStudioTenantId = audience, - ExcludeInteractiveBrowserCredential = true // Force disabled, even though it's disabled by default to respect driver specifications. - }; - - // Optionally set clientId when available - if (clientId is not null) - { - defaultAzureCredentialOptions.ManagedIdentityClientId = clientId; - defaultAzureCredentialOptions.SharedTokenCacheUsername = clientId; - } - AccessToken accessToken = await new DefaultAzureCredential(defaultAzureCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token).ConfigureAwait(false); + // Cache DefaultAzureCredenial based on scope, authority, audience, and clientId + TokenCredentialKey tokenCredentialKey = new(typeof(DefaultAzureCredential), authority, scope, audience, clientId); + AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, string.Empty, tokenRequestContext, cts.Token).ConfigureAwait(false); 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) }; + TokenCredentialOptions tokenCredentialOptions = new() { AuthorityHost = new Uri(authority) }; if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryMSI) { - AccessToken accessToken = await new ManagedIdentityCredential(clientId, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token).ConfigureAwait(false); + // Cache ManagedIdentityCredential based on scope, authority, and clientId + TokenCredentialKey tokenCredentialKey = new(typeof(ManagedIdentityCredential), authority, scope, string.Empty, clientId); + AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, string.Empty, tokenRequestContext, cts.Token).ConfigureAwait(false); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Managed Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn); return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); } @@ -184,11 +180,12 @@ public override async Task AcquireTokenAsync(SqlAuthenti AuthenticationResult result = null; if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal) { - AccessToken accessToken = await new ClientSecretCredential(audience, parameters.UserId, parameters.Password, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token).ConfigureAwait(false); + // Cache ClientSecretCredential based on scope, authority, audience, and clientId + TokenCredentialKey tokenCredentialKey = new(typeof(ClientSecretCredential), authority, scope, audience, clientId); + AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, parameters.Password, tokenRequestContext, cts.Token).ConfigureAwait(false); 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); } - /* * Today, MSAL.NET uses another redirect URI by default in desktop applications that run on Windows * (urn:ietf:wg:oauth:2.0:oob). In the future, we'll want to change this default, so we recommend @@ -204,7 +201,7 @@ public override async Task AcquireTokenAsync(SqlAuthenti redirectUri = "http://localhost"; } #endif - PublicClientAppKey pcaKey = new PublicClientAppKey(parameters.Authority, redirectUri, _applicationClientId + PublicClientAppKey pcaKey = new(parameters.Authority, redirectUri, _applicationClientId #if NETFRAMEWORK , _iWin32WindowFunc #endif @@ -213,7 +210,7 @@ public override async Task AcquireTokenAsync(SqlAuthenti #endif ); - IPublicClientApplication app = GetPublicClientAppInstance(pcaKey); + IPublicClientApplication app = await GetPublicClientAppInstanceAsync(pcaKey, cts.Token).ConfigureAwait(false); if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryIntegrated) { @@ -248,7 +245,7 @@ public override async Task AcquireTokenAsync(SqlAuthenti if (null != previousPw && previousPw is byte[] previousPwBytes && // Only get the cached token if the current password hash matches the previously used password hash - currPwHash.SequenceEqual(previousPwBytes)) + AreEqual(currPwHash, previousPwBytes)) { result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false); } @@ -353,7 +350,7 @@ private static async Task AcquireTokenInteractiveDeviceFlo { if (authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive) { - CancellationTokenSource ctsInteractive = new CancellationTokenSource(); + CancellationTokenSource ctsInteractive = new(); #if NETCOREAPP /* * On .NET Core, MSAL will start the system browser as a separate process. MSAL does not have control over this browser, @@ -447,16 +444,69 @@ public Task AcquireAuthorizationCodeAsync(Uri authorizationUri, Uri redirec => _acquireAuthorizationCodeAsyncCallback.Invoke(authorizationUri, redirectUri, cancellationToken); } - private IPublicClientApplication GetPublicClientAppInstance(PublicClientAppKey publicClientAppKey) + private async Task GetPublicClientAppInstanceAsync(PublicClientAppKey publicClientAppKey, CancellationToken cancellationToken) { if (!s_pcaMap.TryGetValue(publicClientAppKey, out IPublicClientApplication clientApplicationInstance)) { - clientApplicationInstance = CreateClientAppInstance(publicClientAppKey); - s_pcaMap.TryAdd(publicClientAppKey, clientApplicationInstance); + await s_pcaMapModifierSemaphore.WaitAsync(cancellationToken); + try + { + // Double-check in case another thread added it while we waited for the semaphore + if (!s_pcaMap.TryGetValue(publicClientAppKey, out clientApplicationInstance)) + { + clientApplicationInstance = CreateClientAppInstance(publicClientAppKey); + s_pcaMap.TryAdd(publicClientAppKey, clientApplicationInstance); + } + } + finally + { + s_pcaMapModifierSemaphore.Release(); + } } + return clientApplicationInstance; } + private static async Task GetTokenAsync(TokenCredentialKey tokenCredentialKey, string secret, + TokenRequestContext tokenRequestContext, CancellationToken cancellationToken) + { + if (!s_tokenCredentialMap.TryGetValue(tokenCredentialKey, out TokenCredentialData tokenCredentialInstance)) + { + await s_tokenCredentialMapModifierSemaphore.WaitAsync(cancellationToken); + try + { + // Double-check in case another thread added it while we waited for the semaphore + if (!s_tokenCredentialMap.TryGetValue(tokenCredentialKey, out tokenCredentialInstance)) + { + tokenCredentialInstance = CreateTokenCredentialInstance(tokenCredentialKey, secret); + s_tokenCredentialMap.TryAdd(tokenCredentialKey, tokenCredentialInstance); + } + } + finally + { + s_tokenCredentialMapModifierSemaphore.Release(); + } + } + + if (!AreEqual(tokenCredentialInstance._secretHash, GetHash(secret))) + { + // If the secret hash has changed, we need to remove the old token credential instance and create a new one. + await s_tokenCredentialMapModifierSemaphore.WaitAsync(cancellationToken); + try + { + s_tokenCredentialMap.TryRemove(tokenCredentialKey, out _); + tokenCredentialInstance = CreateTokenCredentialInstance(tokenCredentialKey, secret); + s_tokenCredentialMap.TryAdd(tokenCredentialKey, tokenCredentialInstance); + } + finally + { + s_tokenCredentialMapModifierSemaphore.Release(); + } + } + + return await tokenCredentialInstance._tokenCredential.GetTokenAsync(tokenRequestContext, cancellationToken); + } + private static string GetAccountPwCacheKey(SqlAuthenticationParameters parameters) { return parameters.Authority + "+" + parameters.UserId; @@ -470,6 +520,24 @@ private static byte[] GetHash(string input) return hashedBytes; } + private static bool AreEqual(byte[] a1, byte[] a2) + { + if (ReferenceEquals(a1, a2)) + { + return true; + } + else if (a1 is null || a2 is null) + { + return false; + } + else if (a1.Length != a2.Length) + { + return false; + } + + return a1.AsSpan().SequenceEqual(a2.AsSpan()); + } + private IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publicClientAppKey) { IPublicClientApplication publicClientApplication; @@ -513,6 +581,59 @@ private IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publ return publicClientApplication; } + private static TokenCredentialData CreateTokenCredentialInstance(TokenCredentialKey tokenCredentialKey, string secret) + { + if (tokenCredentialKey._tokenCredentialType == typeof(DefaultAzureCredential)) + { + DefaultAzureCredentialOptions defaultAzureCredentialOptions = new() + { + AuthorityHost = new Uri(tokenCredentialKey._authority), + SharedTokenCacheTenantId = tokenCredentialKey._audience, + VisualStudioCodeTenantId = tokenCredentialKey._audience, + VisualStudioTenantId = tokenCredentialKey._audience, + ExcludeInteractiveBrowserCredential = true // Force disabled, even though it's disabled by default to respect driver specifications. + }; + + // Optionally set clientId when available + if (tokenCredentialKey._clientId is not null) + { + defaultAzureCredentialOptions.ManagedIdentityClientId = tokenCredentialKey._clientId; + defaultAzureCredentialOptions.SharedTokenCacheUsername = tokenCredentialKey._clientId; + defaultAzureCredentialOptions.WorkloadIdentityClientId = tokenCredentialKey._clientId; + } + + return new TokenCredentialData(new DefaultAzureCredential(defaultAzureCredentialOptions), GetHash(secret)); + } + + TokenCredentialOptions tokenCredentialOptions = new() { AuthorityHost = new Uri(tokenCredentialKey._authority) }; + + if (tokenCredentialKey._tokenCredentialType == typeof(ManagedIdentityCredential)) + { + return new TokenCredentialData(new ManagedIdentityCredential(tokenCredentialKey._clientId, tokenCredentialOptions), GetHash(secret)); + } + else if (tokenCredentialKey._tokenCredentialType == typeof(ClientSecretCredential)) + { + return new TokenCredentialData(new ClientSecretCredential(tokenCredentialKey._audience, tokenCredentialKey._clientId, secret, tokenCredentialOptions), GetHash(secret)); + } + else if (tokenCredentialKey._tokenCredentialType == typeof(WorkloadIdentityCredential)) + { + // The WorkloadIdentityCredentialOptions object initialization populates its instance members + // from the environment variables AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE, + // and AZURE_ADDITIONALLY_ALLOWED_TENANTS. AZURE_CLIENT_ID may be overridden by the User Id. + WorkloadIdentityCredentialOptions options = new() { AuthorityHost = new Uri(tokenCredentialKey._authority) }; + + if (tokenCredentialKey._clientId is not null) + { + options.ClientId = tokenCredentialKey._clientId; + } + + return new TokenCredentialData(new WorkloadIdentityCredential(options), GetHash(secret)); + } + + // This should never be reached, but if it is, throw an exception that will be noticed during development + throw new ArgumentException(nameof(ActiveDirectoryAuthenticationProvider)); + } + internal class PublicClientAppKey { public readonly string _authority; @@ -572,5 +693,52 @@ public override int GetHashCode() => Tuple.Create(_authority, _redirectUri, _app #endif ).GetHashCode(); } + + internal class TokenCredentialData + { + public TokenCredential _tokenCredential; + public byte[] _secretHash; + + public TokenCredentialData(TokenCredential tokenCredential, byte[] secretHash) + { + _tokenCredential = tokenCredential; + _secretHash = secretHash; + } + } + + internal class TokenCredentialKey + { + public readonly Type _tokenCredentialType; + public readonly string _authority; + public readonly string _scope; + public readonly string _audience; + public readonly string _clientId; + + public TokenCredentialKey(Type tokenCredentialType, string authority, string scope, string audience, string clientId) + { + _tokenCredentialType = tokenCredentialType; + _authority = authority; + _scope = scope; + _audience = audience; + _clientId = clientId; + } + + public override bool Equals(object obj) + { + if (obj != null && obj is TokenCredentialKey tcKey) + { + return string.CompareOrdinal(nameof(_tokenCredentialType), nameof(tcKey._tokenCredentialType)) == 0 + && string.CompareOrdinal(_authority, tcKey._authority) == 0 + && string.CompareOrdinal(_scope, tcKey._scope) == 0 + && string.CompareOrdinal(_audience, tcKey._audience) == 0 + && string.CompareOrdinal(_clientId, tcKey._clientId) == 0 + ; + } + return false; + } + + public override int GetHashCode() => Tuple.Create(_tokenCredentialType, _authority, _scope, _audience, _clientId).GetHashCode(); + } + } } From 08c72cd1ea0e6ed57abbb67872f3734f7c991d8c Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 19 Aug 2024 09:56:42 -0700 Subject: [PATCH 39/76] [5.1] Backport SourceLink translation (#2780) --- src/Directory.Build.props | 15 +++++++ ...waysEncrypted.AzureKeyVaultProvider.csproj | 1 - .../src/Microsoft.Data.SqlClient.csproj | 1 - .../netfx/src/Microsoft.Data.SqlClient.csproj | 1 - .../Microsoft.SqlServer.Server.csproj | 3 -- tools/props/Versions.props | 1 - tools/targets/RepositoryInfo.targets | 40 +++++++++++++++++++ 7 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 tools/targets/RepositoryInfo.targets diff --git a/src/Directory.Build.props b/src/Directory.Build.props index afcf503118..fffaabb889 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -67,6 +67,19 @@ $(DefineConstants);ENCLAVE_SIMULATOR
+ + + portable + true + false + true + true + false + + + true + + @@ -74,4 +87,6 @@ + + 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 3f2267451e..52ffd81109 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 @@ -22,7 +22,6 @@
- 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 08af099e22..59e6b8fbed 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -981,7 +981,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 1a8063169c..2e7d8f086e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -717,7 +717,6 @@ - $(SystemTextEncodingsWebVersion) diff --git a/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj b/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj index dafc9a4dbe..2808094a25 100644 --- a/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj +++ b/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj @@ -40,9 +40,6 @@ Strings.Designer.cs - - - diff --git a/tools/props/Versions.props b/tools/props/Versions.props index f6abe583f4..203f23b361 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -33,7 +33,6 @@ 6.35.0 4.5.1 6.0.0 - 1.1.0 diff --git a/tools/targets/RepositoryInfo.targets b/tools/targets/RepositoryInfo.targets new file mode 100644 index 0000000000..dec4f95206 --- /dev/null +++ b/tools/targets/RepositoryInfo.targets @@ -0,0 +1,40 @@ + + + + + + false + false + false + + + + + + + + <_TranslateUrlPattern>https://[^/]+/ADO.Net/_git/([^/-]+)-(.+) + <_TranslateUrlReplacement>https://github.com/dotnet/SqlClient + + + + + + $([System.Text.RegularExpressions.Regex]::Replace($(ScmRepositoryUrl), $(_TranslateUrlPattern), $(_TranslateUrlReplacement))) + + + + + + + $([System.Text.RegularExpressions.Regex]::Replace(%(SourceRoot.ScmRepositoryUrl), + $(_TranslateUrlPattern), $(_TranslateUrlReplacement))) + + + + + From 2eb0ed950e2e83221d2d28028d66cd802acbee36 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Thu, 22 Aug 2024 08:41:39 -0700 Subject: [PATCH 40/76] Ignor Official pipelines delay signed (#2795) --- .../netcore/ref/Microsoft.Data.SqlClient.csproj | 1 - .../netcore/src/Microsoft.Data.SqlClient.csproj | 1 - .../netfx/ref/Microsoft.Data.SqlClient.csproj | 1 - .../netfx/src/Microsoft.Data.SqlClient.csproj | 1 - 4 files changed, 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index 5df3580b25..b1f7022da5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -14,7 +14,6 @@ true - true $(SigningKeyPath) $(SigningKeyPath) 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 59e6b8fbed..636dc9c706 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -21,7 +21,6 @@ true - true $(SigningKeyPath) $(SigningKeyPath) diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 8e70505f57..293ca6a8bf 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -10,7 +10,6 @@ true - true $(SigningKeyPath) $(SigningKeyPath) 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 2e7d8f086e..2439554cff 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -61,7 +61,6 @@ true - true $(SigningKeyPath) $(SigningKeyPath) From 51950770246e5e3fae350ea55ca9397b5f2de677 Mon Sep 17 00:00:00 2001 From: dauinsight <145612907+dauinsight@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:18:55 -0700 Subject: [PATCH 41/76] Fix decrypt failure to drain data (#2818) Co-authored-by: Aris Rellegue <134557572+arellegue@users.noreply.github.com> --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 6 ++++++ .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 6 ++++++ 2 files changed, 12 insertions(+) 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 17327f16e1..dae197f5f7 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 @@ -6090,6 +6090,12 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T // call to decrypt column keys has failed. The data wont be decrypted. // Not setting the value to false, forces the driver to look for column value. // Packet received from Key Vault will throws invalid token header. + if (stateObj.HasPendingData) + { + // Drain the pending data now if setting the HasPendingData to false. + // SqlDataReader.TryCloseInternal can not drain if HasPendingData = false. + DrainData(stateObj); + } stateObj.HasPendingData = false; } throw SQL.ColumnDecryptionFailed(columnName, null, e); 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 e143b13dfc..c94f714973 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 @@ -6910,6 +6910,12 @@ internal bool TryReadSqlValue(SqlBuffer value, // call to decrypt column keys has failed. The data wont be decrypted. // Not setting the value to false, forces the driver to look for column value. // Packet received from Key Vault will throws invalid token header. + if (stateObj._pendingData) + { + // Drain the pending data now if setting the HasPendingData to false. + // SqlDataReader.TryCloseInternal can not drain if HasPendingData = false. + DrainData(stateObj); + } stateObj._pendingData = false; } throw SQL.ColumnDecryptionFailed(columnName, null, e); From cec5dd8df551733f291c5ee720212bc37d2eae2a Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 26 Aug 2024 19:32:08 -0700 Subject: [PATCH 42/76] [5.1] Eng | Add strong name validation to package validator (#2802) (#2804) --- .../jobs/validate-signed-package-job.yml | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml index bd15734e22..fd613d230e 100644 --- a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml +++ b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml @@ -103,6 +103,42 @@ jobs: nuget verify -All $(pathToDownloadedNuget)\*.nupkg displayName: 'Verify nuget signature' + - powershell: | + if($env:CDP_BUILD_TYPE -eq 'Official') + { + # Recursively find all .dll files in TempFolder (installed nuget folder) + # Microsoft.Data.SqlClient.dll and Microsoft.Data.SqlClient.resources.dll (in localized folders) should have strong name + $dllFiles = Get-ChildItem -Path $(TempFolderName) -Recurse -Filter *.dll + $badDlls = @() + foreach ($file in $dllFiles) + { + # Run sn.k to verify the strong name on each dll + $result = & "C:\Program Files (x86)\Microsoft SDKs\Windows\*\bin\NETFX 4.8.1 Tools\sn.exe" -vf $file.FullName + Write-OutPut $result + + # if thhe dll is not valid, it would be delay signed or test-signed which is not meant for production + if($result[$result.Length-1] -notlike "* is valid") + { + $badDlls += $result[$result.Length-1] + } + } + if($badDlls.Count -gt 0) + { + Write-OutPut "Error: Invalid dlls are detected. Chek below list:" + foreach($dll in $badDlls) + { + Write-Output $dll + } + Exit -1 + } + Write-Host "Strong name has been verified for all dlls" + } + else + { + Write-OutPut "Strong name verification is not required for non-official builds" + } + displayName: 'Verify strong name is generated for production' + - powershell: | # Checks the expected folder names such as lib, ref, runtimes Get-ChildItem -Path $(extractedNugetPath) -Directory | select Name | foreach { @@ -205,7 +241,6 @@ jobs: } } displayName: 'Verify all DLLs unzipped match "expected" hierarchy' - - powershell: | # Verify all dlls status are Valid From 00092102832f8bce2580a85d124751051faa3e75 Mon Sep 17 00:00:00 2001 From: dauinsight <145612907+dauinsight@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:50:47 -0700 Subject: [PATCH 43/76] [5.1] | Fix strong name in AKV provider (#2846) --- .../steps/build-all-configurations-signed-dlls-step.yml | 4 ++-- .../common/templates/steps/code-analyze-step.yml | 2 +- ...SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj | 8 ++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml b/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml index 647c0ff35f..36bc57c7da 100644 --- a/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml +++ b/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml @@ -44,14 +44,14 @@ steps: inputs: solution: '**/build.proj' configuration: '$(Configuration)' - msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAKVNetStAllOS -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -p:SignAssembly=true -p:AssemblyOriginatorKeyFile=$(Agent.TempDirectory)\netfxKeypair.snk' + msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAKVNetStAllOS -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk' - task: MSBuild@1 displayName: 'BuildAKVNetFx using build.proj' inputs: solution: '**/build.proj' configuration: '$(Configuration)' - msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAKVNetFx -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -p:SignAssembly=true -p:AssemblyOriginatorKeyFile=$(Agent.TempDirectory)\netfxKeypair.snk' + msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAKVNetFx -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk' - task: MSBuild@1 displayName: 'BuildAKVNetCoreAllOS using build.proj' diff --git a/eng/pipelines/common/templates/steps/code-analyze-step.yml b/eng/pipelines/common/templates/steps/code-analyze-step.yml index 55111a42fe..918bc273fa 100644 --- a/eng/pipelines/common/templates/steps/code-analyze-step.yml +++ b/eng/pipelines/common/templates/steps/code-analyze-step.yml @@ -42,7 +42,7 @@ steps: msBuildVersion: 17.0 msBuildArchitecture: x64 setupCommandlinePicker: vs2022 - msBuildCommandline: 'msbuild ${{parameters.sourceRoot}}\build.proj -p:configuration=Release -p:GenerateNuget=false -p:BuildTools=false -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -t:BuildAKVNetCoreAllOS' + msBuildCommandline: 'msbuild ${{parameters.sourceRoot}}\build.proj -p:configuration=Release -p:GenerateNuget=false -p:BuildTools=false -p:NugetPackageVersion=${{parameters.nugetPackageRefVersion }} -p:ReferenceType=Package -t:BuildAKVNetCoreAllOS -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk' - ${{ if or(eq(parameters.analyzeType, 'inspect'), eq(parameters.analyzeType, 'all')) }}: - task: securedevelopmentteam.vss-secure-development-tools.build-task-codeinspector.CodeInspector@2 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 52ffd81109..ca570955ca 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 @@ -15,6 +15,14 @@ false MIT true + + + + true + $(SigningKeyPath) + + + $(SigningKeyPath) From f6b7d7cfbaa0ad541f707f0db9e7eb423151446a Mon Sep 17 00:00:00 2001 From: dauinsight <145612907+dauinsight@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:11:21 -0700 Subject: [PATCH 44/76] [5.1] Eng | Add CDP_BUILD_TYPE to package validator (#2858) --- .../common/templates/jobs/build-signed-package-job.yml | 4 ++++ .../templates/jobs/validate-signed-package-job.yml | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml index f07445c291..bfd392b186 100644 --- a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml +++ b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml @@ -28,6 +28,10 @@ jobs: - script: SET displayName: 'Print Environment Variables' + - powershell: | + Write-Host "##vso[task.setvariable variable=CDP_BUILD_TYPE_COPY;isOutput=true]$($env:CDP_BUILD_TYPE)" + name: GetBuildType + - template: ../steps/build-all-configurations-signed-dlls-step.yml@self - template: ../steps/code-analyze-step.yml@self diff --git a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml index fd613d230e..1439aa1bb1 100644 --- a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml +++ b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml @@ -54,12 +54,15 @@ jobs: - name: pathToDownloadedNuget # path to the downloaded nuget files value: $(Pipeline.Workspace)\${{parameters.packageFolderName }} + - name: BuildType + value: $[ stageDependencies.buildMDS.build_signed_package.outputs['GetBuildType.CDP_BUILD_TYPE_COPY'] ] + steps: - script: SET displayName: 'Print Environment Variables' - task: NuGetToolInstaller@1 - displayName: 'Use NuGet ' + displayName: 'Use NuGet' - powershell: | #Sets Variables for AssemblyFileVersion, AssemblyVersion and NugetPackageVersion @@ -104,7 +107,9 @@ jobs: displayName: 'Verify nuget signature' - powershell: | - if($env:CDP_BUILD_TYPE -eq 'Official') + $buildType = [string]"$(BuildType)" + + if($buildType -eq 'Official') { # Recursively find all .dll files in TempFolder (installed nuget folder) # Microsoft.Data.SqlClient.dll and Microsoft.Data.SqlClient.resources.dll (in localized folders) should have strong name From 4acabbad7e3521935cab06ed8d28e34ad39eb5e5 Mon Sep 17 00:00:00 2001 From: samsharma2700 Date: Wed, 11 Dec 2024 13:46:14 -0800 Subject: [PATCH 45/76] Fix CVE issue (#3077) --- .../Microsoft.DotNet.GenAPI/Microsoft.DotNet.GenAPI.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/GenAPI/Microsoft.DotNet.GenAPI/Microsoft.DotNet.GenAPI.csproj b/tools/GenAPI/Microsoft.DotNet.GenAPI/Microsoft.DotNet.GenAPI.csproj index 8bfcd7ad1e..b0320948d3 100644 --- a/tools/GenAPI/Microsoft.DotNet.GenAPI/Microsoft.DotNet.GenAPI.csproj +++ b/tools/GenAPI/Microsoft.DotNet.GenAPI/Microsoft.DotNet.GenAPI.csproj @@ -15,6 +15,7 @@ + From 38696c583d1458de4c5f7ebb38a5cfd676b3b68d Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Fri, 24 Jan 2025 21:33:13 +0530 Subject: [PATCH 46/76] Move NuGet.config to the root + add vuln info (#3123) Ports PRs: https://github.com/dotnet/SqlClient/pull/2443 and https://github.com/dotnet/SqlClient/pull/3024 to release/5.1 branch --- src/NuGet.config => NuGet.config | 4 ++++ src/Directory.Build.props | 6 ++++++ 2 files changed, 10 insertions(+) rename src/NuGet.config => NuGet.config (67%) diff --git a/src/NuGet.config b/NuGet.config similarity index 67% rename from src/NuGet.config rename to NuGet.config index 5832a9da27..4f39716bf4 100644 --- a/src/NuGet.config +++ b/NuGet.config @@ -4,4 +4,8 @@ + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index fffaabb889..c47b9e55a3 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -61,12 +61,18 @@ $(NuGetRoot)nuget.exe true + $(WarningsNotAsErrors);NU1901;NU1902;NU1903;NU1904;NU1905 false $(DefineConstants);ENCLAVE_SIMULATOR + + + all + + portable From 081c4bf3ab17820202a5ed26db3991236b1d7ce2 Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Thu, 6 Feb 2025 10:36:58 -0800 Subject: [PATCH 47/76] Fix local feed step to look in root for nuget config. (#3143) --- .../templates/steps/update-nuget-config-local-feed-step.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml b/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml index 61bd394af4..5258a3941d 100644 --- a/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml +++ b/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml @@ -27,13 +27,13 @@ steps: #Set the Nuget.config file in the project to use extracted package $rootFolder = Get-location - [Xml] $nugetConfig = Get-Content -Path "src\Nuget.config" + [Xml] $nugetConfig = Get-Content -Path "Nuget.config" $Value = Resolve-Path ${{parameters.downloadedNugetPath }} $newAdd = $nugetConfig.CreateElement("add") $newAdd.SetAttribute("key","Package source") $newAdd.SetAttribute("value", "$Value\" ) $nugetConfig.configuration.packageSources.AppendChild($newAdd) - $nugetConfig.Save("$rootFolder\src\Nuget.config") + $nugetConfig.Save("$rootFolder\Nuget.config") displayName: 'Update NuGet config file to read from Nuget folder' - task: MSBuild@1 From 6ec0741f9fe9c01c7e0c8ea1ebcf2bd9bff753c8 Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Tue, 25 Feb 2025 18:17:25 -0600 Subject: [PATCH 48/76] Sync ref project references with runtime projects (#3180) Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> --- .../ref/Microsoft.Data.SqlClient.csproj | 33 ++++++++++++++++++- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 17 ++++++++++ .../netfx/src/Microsoft.Data.SqlClient.csproj | 26 ++++----------- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index b1f7022da5..63226c9cbc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -3,6 +3,9 @@ false net6.0;netstandard2.0;netstandard2.1 netstandard2.1 + $(OS) + true + true $(ObjFolder)$(Configuration)\$(AssemblyName)\ref\ $(BinFolder)$(Configuration)\$(AssemblyName)\ref\ $(OutputPath)\$(TargetFramework)\Microsoft.Data.SqlClient.xml @@ -24,13 +27,41 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 293ca6a8bf..db07f36d98 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -20,10 +20,27 @@ + + + + + + + + + + $(MicrosoftDataSqlClientSniVersion) + All + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + \ No newline at end of file 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 2439554cff..7bd61421e7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -92,8 +92,8 @@ - + @@ -716,29 +716,17 @@ - - $(SystemTextEncodingsWebVersion) - + $(MicrosoftDataSqlClientSniVersion) All runtime; build; native; contentfiles; analyzers; buildtransitive - - $(AzureIdentityVersion) - - - $(MicrosoftIdentityClientVersion) - - - $(MicrosoftIdentityModelProtocolsOpenIdConnectVersion) - - - $(MicrosoftIdentityModelJsonWebTokensVersion) - - - $(SystemBuffersVersion) - + + + + + From 87f6812465fd6310957f2e10858af2432a7c8ea5 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Fri, 14 Mar 2025 13:43:14 -0700 Subject: [PATCH 49/76] Update Microsoft.Extensions.Hostings (#3207) --- tools/props/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 203f23b361..6dd58077d1 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -73,7 +73,7 @@ 10.50.1600.1 0.13.2 6.0.0 - 6.0.0 + 6.0.1 $(NugetPackageVersion) From d72e731f03f9f687c505b80d6288732cd0eb87c8 Mon Sep 17 00:00:00 2001 From: dauinsight <145612907+dauinsight@users.noreply.github.com> Date: Wed, 2 Apr 2025 08:24:56 -0700 Subject: [PATCH 50/76] [5.1] Fix | Dotnet build command (#2728) * Test genapi csproj * remove quote --- build.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.proj b/build.proj index 1b3372bc95..1f295abc94 100644 --- a/build.proj +++ b/build.proj @@ -105,7 +105,7 @@ - $(DotNetCmd) dotnet build -c Release -p:ReferenceType=$(ReferenceType)" + $(DotNetCmd) dotnet build -c Release -p:ReferenceType=$(ReferenceType) From c9777909bc5704b1256b644aeb31d4f99c04b637 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Wed, 2 Apr 2025 08:25:22 -0700 Subject: [PATCH 51/76] [Release 5.1] Update MS Extensions Hosting package (#3068) * Update MS Extensions Hosting package * Update Microsoft.Extensions.Caching.Memory to 6.0.3 --- .../FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj | 2 +- tools/props/Versions.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index a90960bdc5..fcc368dc8a 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -66,7 +66,7 @@ - + diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 6dd58077d1..55c18c23c2 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -56,7 +56,7 @@ [1.38.0,2.0.0) [4.4.0,5.0.0) - 6.0.1 + 6.0.3 @@ -73,7 +73,7 @@ 10.50.1600.1 0.13.2 6.0.0 - 6.0.1 + 6.0.1 $(NugetPackageVersion) From 60fc6619764e4488c2b1b15d221d2a7ddeb9a909 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:46:25 -0700 Subject: [PATCH 52/76] ESRP federated credential update (move to AME) (#3261) (#3267) --- .../steps/esrp-code-signing-step.yml | 46 +++++++++++++------ eng/pipelines/libraries/common-variables.yml | 12 +++-- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml b/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml index 67e21ed7ff..b1e14aa604 100644 --- a/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml +++ b/eng/pipelines/common/templates/steps/esrp-code-signing-step.yml @@ -17,6 +17,10 @@ parameters: type: string default: $(artifactDirectory) + - name: ESRPConnectedServiceName + type: string + default: $(ESRPConnectedServiceName) + - name: appRegistrationClientId type: string default: $(appRegistrationClientId) @@ -25,16 +29,28 @@ parameters: type: string default: $(appRegistrationTenantId) + - name: AuthAKVName + type: string + default: $(AuthAKVName) + + - name: AuthSignCertName + type: string + default: $(AuthSignCertName) + + - name: EsrpClientId + type: string + default: $(EsrpClientId) + steps: - ${{ if eq(parameters.artifactType, 'dll') }}: - task: EsrpMalwareScanning@5 displayName: 'ESRP MalwareScanning' inputs: - ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - AuthAKVName: SqlClientDrivers - AuthCertName: 'ESRP-Release-Auth' + EsrpClientId: '${{parameters.EsrpClientId }}' + UseMSIAuthentication: true FolderPath: '${{parameters.sourceRoot }}' Pattern: '*.dll' CleanupTempStorage: 1 @@ -42,12 +58,13 @@ steps: - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning' inputs: - ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - AuthAKVName: SqlClientDrivers - AuthCertName: 'ESRP-Release-Auth' - AuthSignCertName: 'ESRP-Release-Sign2' + EsrpClientId: '${{parameters.EsrpClientId }}' + UseMSIAuthentication: true + AuthAKVName: '${{parameters.AuthAKVName }}' + AuthSignCertName: '${{parameters.AuthSignCertName }}' FolderPath: '${{parameters.sourceRoot }}' Pattern: '*.dll' signConfigType: inlineSignParams @@ -94,11 +111,11 @@ steps: - task: EsrpMalwareScanning@5 displayName: 'ESRP MalwareScanning Nuget Package' inputs: - ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - AuthAKVName: SqlClientDrivers - AuthCertName: 'ESRP-Release-Auth' + EsrpClientId: '${{parameters.EsrpClientId }}' + UseMSIAuthentication: true FolderPath: '${{parameters.artifactDirectory }}' Pattern: '*.nupkg' CleanupTempStorage: 1 @@ -107,12 +124,13 @@ steps: displayName: 'ESRP CodeSigning Nuget Package' inputs: inputs: - ConnectedServiceName: 'ESRP Workload Identity federation service-ADO.Net' + ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - AuthAKVName: SqlClientDrivers - AuthCertName: 'ESRP-Release-Auth' - AuthSignCertName: 'ESRP-Release-Sign2' + EsrpClientId: '${{parameters.EsrpClientId }}' + UseMSIAuthentication: true + AuthAKVName: '${{parameters.AuthAKVName }}' + AuthSignCertName: '${{parameters.AuthSignCertName }}' FolderPath: '${{parameters.artifactDirectory }}' Pattern: '*.nupkg' signConfigType: inlineSignParams diff --git a/eng/pipelines/libraries/common-variables.yml b/eng/pipelines/libraries/common-variables.yml index 7b2bc00cb1..718633691b 100644 --- a/eng/pipelines/libraries/common-variables.yml +++ b/eng/pipelines/libraries/common-variables.yml @@ -5,6 +5,14 @@ ################################################################################# variables: + - group: ESRP Federated Creds (AME) + # ESRPConnectedServiceName + # ESRPClientId + # AppRegistrationClientId + # AppRegistrationTenantId + # AuthAKVName + # AuthSignCertName + - name: Configuration value: Release - name: CommitHead @@ -17,7 +25,3 @@ variables: value: $(REPOROOT)/symbols - name: artifactDirectory value: '$(REPOROOT)/packages' - - name: appRegistrationClientId - value: 'a0d18a38-fde1-4ba7-92e1-15be16cb6a8e' - - name: appRegistrationTenantId - value: '72f988bf-86f1-41af-91ab-2d7cd011db47' From d47c51dbf443d64643fb5a3a19162dd57f6ee8dc Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 17 Apr 2025 17:13:47 -0300 Subject: [PATCH 53/76] [5.1] CVE-2024-43485 - Update System.Text.Json to 6.0.11 (#3279) --- .config/tsaoptions.json | 2 +- .../dotnet-sqlclient-signing-pipeline.yml | 2 +- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 9 ++++++--- .../netfx/src/Microsoft.Data.SqlClient.csproj | 7 +++++-- tools/props/Versions.props | 3 ++- tools/specs/Microsoft.Data.SqlClient.nuspec | 9 +++++---- ...lwaysEncrypted.AzureKeyVaultProvider.nuspec | 18 +++++++++--------- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json index c9e3a2cfb6..077ef21c76 100644 --- a/.config/tsaoptions.json +++ b/.config/tsaoptions.json @@ -7,7 +7,7 @@ "repositoryName": "SqlClient", "codebaseName": "SqlClient", "allTools": true, - "template": "MSDATA_RevolutionR", + "template": "MSDATA_RevolutionR_Overloaded0", "language": "csharp", "includePathPatterns": "src/Microsoft.Data.SqlClient/*, src/Microsoft.SqlServer.Server/*, tools/*", "excludePathPatterns": "src/Microsoft.Data.SqlClient/tests/*" diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml index 6e9cfe5483..10b9dc7da3 100644 --- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml @@ -46,7 +46,7 @@ parameters: # parameters are shown up in ADO UI in a build queue time - name: MDS_PackageRef_Version displayName: 'MDS package version of AKV Provider (build AKV)' type: string - default: 3.0.0 + default: 5.1.2 - name: CurrentNetFxVersion displayName: 'Lowest supported .NET Framework version (MDS validation)' diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index db07f36d98..ba22087d50 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -31,16 +31,19 @@ - $(MicrosoftDataSqlClientSniVersion) All runtime; build; native; contentfiles; analyzers; buildtransitive + + - + + + - \ No newline at end of file + 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 7bd61421e7..cd49858ff6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -716,17 +716,20 @@ - $(MicrosoftDataSqlClientSniVersion) All runtime; build; native; contentfiles; analyzers; buildtransitive + + - + + + diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 55c18c23c2..425cf07f4a 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -32,7 +32,8 @@ 6.35.0 6.35.0 4.5.1 - 6.0.0 + 6.0.1 + 6.0.11 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 006091a196..af67dbe06b 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -36,7 +36,8 @@ When using NuGet 3.x this package requires at least version 3.4. - + + @@ -49,7 +50,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -65,7 +66,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -81,7 +82,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 1c5e78000b..2a18f80358 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -25,25 +25,25 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti sqlclient microsoft.data.sqlclient azurekeyvaultprovider akvprovider alwaysencrypted - + - + - + - + - + - + - + - + - + From bd440519bc9eecc14b7cad873d0476d5babbb5dd Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 18 Apr 2025 21:12:21 -0300 Subject: [PATCH 54/76] Handle null socket when receiving packets of data (#3270) (#3285) Co-authored-by: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> --- .../Data/SqlClient/SNI/SNITcpHandle.cs | 27 ++++++++++++++----- .../netcore/src/Resources/Strings.Designer.cs | 9 +++++++ .../netcore/src/Resources/Strings.resx | 5 +++- .../netfx/src/Resources/Strings.Designer.cs | 2 +- .../netfx/src/Resources/Strings.resx | 2 +- 5 files changed, 35 insertions(+), 10 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 103af2ed2d..b8e38978b9 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 @@ -765,17 +765,29 @@ public override uint Send(SNIPacket packet) } /// - /// Receive a packet synchronously + /// Receives a packet synchronously. /// - /// SNI packet - /// Timeout in Milliseconds - /// SNI error code + /// The received SNI packet. + /// + /// Timeout in milliseconds: + /// - If greater than 0, sets the socket's receive timeout to the specified value. + /// - If equal to -1, represents an infinite timeout (socket timeout is set to 0). + /// - If less than -1 or equal to 0, results in a timeout error. + /// + /// SNI error code indicating the result of the operation. public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) { SNIPacket errorPacket; lock (this) { packet = null; + + if (_socket == null) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Socket is null.", args0: _connectionId); + return ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, Strings.SNI_ERROR_10); + } + try { if (timeoutInMilliseconds > 0) @@ -784,8 +796,7 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) } else if (timeoutInMilliseconds == -1) { - // SqlClient internally represents infinite timeout by -1, and for TcpClient this is translated to a timeout of 0 - _socket.ReceiveTimeout = 0; + _socket.ReceiveTimeout = Timeout.Infinite; } else { @@ -840,7 +851,9 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) } finally { - _socket.ReceiveTimeout = 0; + // Reset the socket timeout to Timeout.Infinite after the receive operation is done + // to avoid blocking the thread in case of a timeout error. + _socket.ReceiveTimeout = Timeout.Infinite; } } } 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 bba9987634..67a2aa5922 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -1446,6 +1446,15 @@ internal static string SNI_ERROR_1 { } } + /// + /// Looks up a localized string similar to Socket is null. + /// + internal static string SNI_ERROR_10 { + get { + return ResourceManager.GetString("SNI_ERROR_10", resourceCulture); + } + } + /// /// Looks up a localized string similar to Timeout error. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index 28999773e0..70afac4e7d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -927,6 +927,9 @@ Associating port with I/O completion mechanism failed + + Socket is null + Timeout error @@ -1944,4 +1947,4 @@ SQL Network Interfaces - \ 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 b30f54f8e5..f049de08f3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -8182,7 +8182,7 @@ internal static string SNI_ERROR_1 { } /// - /// Looks up a localized string similar to . + /// Looks up a localized string similar to Socket is null. /// internal static string SNI_ERROR_10 { get { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 85627eae31..88318d6452 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -3805,7 +3805,7 @@ Associating port with I/O completion mechanism failed - + Socket is null Timeout error From 5d51c5475b1509bea66cad921dde891d738a00b9 Mon Sep 17 00:00:00 2001 From: David Engel Date: Fri, 18 Apr 2025 17:15:30 -0700 Subject: [PATCH 55/76] [5.1] Replace example password with ******** (#3289) * Replace password with **** * Replace password with **** * Replace password with **** * Replace password with **** * Replace password with **** --- doc/samples/SqlConnectionStringBuilder.cs | 4 ++-- doc/samples/SqlConnectionStringBuilder3.cs | 2 +- doc/samples/SqlConnectionStringBuilder_IntegratedSecurity.cs | 2 +- doc/samples/SqlConnectionStringBuilder_Remove.cs | 2 +- .../Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/samples/SqlConnectionStringBuilder.cs b/doc/samples/SqlConnectionStringBuilder.cs index f1c3252880..d1d22254f1 100644 --- a/doc/samples/SqlConnectionStringBuilder.cs +++ b/doc/samples/SqlConnectionStringBuilder.cs @@ -21,12 +21,12 @@ static void Main() // connection string, and you can retrieve and // modify any of the elements. builder.ConnectionString = "server=(local);user id=ab;" + - "password= a!Pass113;initial catalog=AdventureWorks"; + "password=********;initial catalog=AdventureWorks"; // Now that the connection string has been parsed, // you can work with individual items. Console.WriteLine(builder.Password); - builder.Password = "new@1Password"; + builder.Password = "********"; // You can refer to connection keys using strings, // as well. When you use this technique (the default diff --git a/doc/samples/SqlConnectionStringBuilder3.cs b/doc/samples/SqlConnectionStringBuilder3.cs index 3e0c253039..fc691489fe 100644 --- a/doc/samples/SqlConnectionStringBuilder3.cs +++ b/doc/samples/SqlConnectionStringBuilder3.cs @@ -10,7 +10,7 @@ static void Main() try { string connectString = - "Server=(local);Database=AdventureWorks;UID=ab;Pwd= a!Pass@@"; + "Server=(local);Database=AdventureWorks;UID=ab;Pwd=********"; Console.WriteLine("Original: " + connectString); SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectString); diff --git a/doc/samples/SqlConnectionStringBuilder_IntegratedSecurity.cs b/doc/samples/SqlConnectionStringBuilder_IntegratedSecurity.cs index 693298dd35..b2e6773559 100644 --- a/doc/samples/SqlConnectionStringBuilder_IntegratedSecurity.cs +++ b/doc/samples/SqlConnectionStringBuilder_IntegratedSecurity.cs @@ -10,7 +10,7 @@ static void Main() try { string connectString = - "Data Source=(local);User ID=ab;Password=MyPassword;" + + "Data Source=(local);User ID=ab;Password=********;" + "Initial Catalog=AdventureWorks"; SqlConnectionStringBuilder builder = diff --git a/doc/samples/SqlConnectionStringBuilder_Remove.cs b/doc/samples/SqlConnectionStringBuilder_Remove.cs index 7de94386a1..835b8906c3 100644 --- a/doc/samples/SqlConnectionStringBuilder_Remove.cs +++ b/doc/samples/SqlConnectionStringBuilder_Remove.cs @@ -10,7 +10,7 @@ static void Main() try { string connectString = - "Data Source=(local);User ID=ab;Password= a1Pass@@11;" + + "Data Source=(local);User ID=ab;Password=********;" + "Initial Catalog=AdventureWorks"; SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectString); diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml index 82f7aa8ec9..6bb436390f 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml @@ -839,7 +839,7 @@ Connections are considered the same if they have the same connection string. Dif The example displays the following text in the console window: ``` -Original: Data Source=(local);Initial Catalog=AdventureWorks;User ID=ab;Password= a1Pass@@11 +Original: Data Source=(local);Initial Catalog=AdventureWorks;User ID=ab;Password=******** Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=True Database = AdventureWorks ``` From cd2e4fb02b652d8c24b2184508f2820a736aa7f5 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Wed, 23 Apr 2025 15:51:01 -0300 Subject: [PATCH 56/76] Updated SNI version to 5.1.2. (#3294) --- .../netcore/ref/Microsoft.Data.SqlClient.csproj | 2 +- .../netcore/src/Microsoft.Data.SqlClient.csproj | 2 +- tools/props/Versions.props | 6 +++--- tools/specs/Microsoft.Data.SqlClient.nuspec | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index 63226c9cbc..912d68f5de 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -54,7 +54,7 @@ - + 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 636dc9c706..f06bc9df7b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -967,7 +967,7 @@ - + diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 425cf07f4a..a2b1d7273a 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -1,7 +1,7 @@ - 5.1.0 + 5.1.7 0 $(MdsVersionDefault).$(BuildNumber) $(AssemblyFileVersion) @@ -23,7 +23,7 @@ - 5.1.1 + 5.1.2 @@ -38,7 +38,7 @@ 5.0.0 - 5.1.1 + 5.1.2 6.0.1 1.0.0 6.0.1 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index af67dbe06b..7e45b87cf0 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 - + @@ -40,7 +40,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -55,7 +55,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -72,7 +72,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + From 7577ba2b355eb4fbebca47886f2af53757c04920 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:36:29 -0300 Subject: [PATCH 57/76] Updated MDS release version variables to 5.1.7. (#3298) --- eng/pipelines/dotnet-sqlclient-signing-pipeline.yml | 12 ++++++++++-- eng/pipelines/libraries/mds-variables.yml | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml index 10b9dc7da3..299a4085fa 100644 --- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml @@ -91,14 +91,22 @@ extends: featureFlags: WindowsHostVersion: 1ESWindows2022 globalSdl: # https://aka.ms/obpipelines/sdl + tsa: + # The OneBranch template will set 'break' to false for the other SDL + # tools when TSA is enabled. This allows TSA to gather the results + # and publish them for downstream analysis. + enabled: ${{parameters.enableAllSdlTools }} apiscan: enabled: ${{parameters.enableAllSdlTools }} + # For non-official builds, the OneBranch template seems to set APIScan's + # 'break' to true even when TSA is enabled. We don't want APIScan to + # break non-official builds, so we explicitly set 'break' to false here. + ${{ if ne(parameters.oneBranchType, 'Official') }}: + break: false softwareFolder: $(softwareFolder) symbolsFolder: $(symbolsFolder) softwarename: Microsoft.Data.SqlClient versionNumber: $(AssemblyFileVersion) - tsa: - enabled: ${{parameters.enableAllSdlTools }} # onebranch publish all sdl results to TSA. If TSA is disabled all SDL tools will forced into 'break' build mode. codeql: compiled: enabled: false #[warning]Consider running CodeQL on the default branch only. diff --git a/eng/pipelines/libraries/mds-variables.yml b/eng/pipelines/libraries/mds-variables.yml index 4dad2e262c..8d5e37ab83 100644 --- a/eng/pipelines/libraries/mds-variables.yml +++ b/eng/pipelines/libraries/mds-variables.yml @@ -12,7 +12,7 @@ variables: - name: Minor value: 1 - name: Patch - value: 6 + value: 7 - name: Packaging.EnableSBOMSigning value: true From 0697f41c005b269145e4e82d285b6183c4a9056a Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:51:26 -0300 Subject: [PATCH 58/76] Added localizations for the new SNI_ERROR_10 code from PR #3285. (#3300) --- .../netfx/src/Resources/Strings.de.resx | 4 ++-- .../netfx/src/Resources/Strings.es.resx | 4 ++-- .../netfx/src/Resources/Strings.fr.resx | 4 ++-- .../netfx/src/Resources/Strings.it.resx | 4 ++-- .../netfx/src/Resources/Strings.ja.resx | 4 ++-- .../netfx/src/Resources/Strings.ko.resx | 4 ++-- .../netfx/src/Resources/Strings.pt-BR.resx | 4 ++-- .../netfx/src/Resources/Strings.ru.resx | 4 ++-- .../netfx/src/Resources/Strings.zh-Hans.resx | 4 ++-- .../netfx/src/Resources/Strings.zh-Hant.resx | 4 ++-- 10 files changed, 20 insertions(+), 20 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 d9aa92e08b..9814565852 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -3805,7 +3805,7 @@ Zuordnung des Anschlusses mit E/A-Abschlussmechanismus fehlgeschlagen - + Socket ist NULL. Timeoutfehler @@ -4635,4 +4635,4 @@ Die Plattform "{0}" wird bei .NET Framework nicht unterstützt. - \ 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 bb98c84037..ed56be833b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -3805,7 +3805,7 @@ Error al completar el mecanismo de asociación de puerto con E/S - + El socket es null Error de tiempo de espera @@ -4635,4 +4635,4 @@ La plataforma '{0}' no se admite cuando el destino es .NET Framework. - \ 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 3e83f37ccf..f1c2a8f54f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -3805,7 +3805,7 @@ Échec de l'association du port avec le mécanisme de terminaison d'E/S - + Le socket est nul Erreur de délai d'attente @@ -4635,4 +4635,4 @@ La plateforme '{0}' n’est pas prise en charge lors du ciblage de .NET Framework. - \ 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 34bdf447e8..3af480b805 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -3805,7 +3805,7 @@ Associazione della porta al meccanismo di completamento I/O non riuscita - + Il socket è null Errore di timeout @@ -4635,4 +4635,4 @@ La piattaforma '{0}' non è supportata quando la destinazione è .NET Framework. - \ 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 b28b582fe6..b20c2434ec 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -3805,7 +3805,7 @@ ポートと I/O 完了メカニズムの関連付けに失敗しました - + ソケットは null です タイムアウト エラー @@ -4635,4 +4635,4 @@ .NET Framework を対象とする場合、'{0}' プラットフォームはサポートされません。 - \ 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 5c3755a573..ff3b07f2aa 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -3805,7 +3805,7 @@ 포트와 입/출력 완료 메커니즘의 연결이 실패했습니다. - + 소켓이 null입니다. 시간 초과 오류입니다. @@ -4635,4 +4635,4 @@ .NET Framework를 대상으로 지정할 때는 '{0}' 플랫폼이 지원되지 않습니다. - \ 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 353ef45af7..6f575dcb01 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 @@ -3805,7 +3805,7 @@ Falha ao associar porta e mecanismo de conclusão de E/S - + O soquete é nulo Erro de tempo limite @@ -4635,4 +4635,4 @@ A plataforma '{0}' não é suportada ao direcionar o .NET Framework. - \ 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 a8d3c14134..93d1b4f376 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -3805,7 +3805,7 @@ Сбой при сопоставлении порта механизму завершения ввода-вывода - + Сокет имеет значение NULL Ошибка времени ожидания @@ -4635,4 +4635,4 @@ Платформа "{0}" не поддерживается, если целью является .NET Framework. - \ 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 f71303097d..8d42310c50 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 @@ -3805,7 +3805,7 @@ 端口与 I/O 关联的完成机制失败 - + 套接字为 null 超时错误 @@ -4635,4 +4635,4 @@ 面向 .NET Framework 时,不支持 "{0}" 平台。 - \ 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 8fab7073e5..70ba001f41 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 @@ -3805,7 +3805,7 @@ 關聯連接埠與 I/O 完成機制失敗 - + 通訊端為 null 逾時錯誤 @@ -4635,4 +4635,4 @@ 以.NET Framework 為目標時,不支援 '{0}' 平台。 - \ No newline at end of file + From 4a6c9f4650216b1b3b00f21d88bab24c3b79f6e1 Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Fri, 2 May 2025 17:21:25 -0500 Subject: [PATCH 59/76] Backport NugetAudit settings (#3318) --- src/Directory.Build.props | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c47b9e55a3..be2fe1f22a 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -67,8 +67,18 @@ $(DefineConstants);ENCLAVE_SIMULATOR - - + + + + + false + all From 5df1ac3796a4791b4c285ad33e6e02bd317c2e7c Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 8 May 2025 10:33:58 -0300 Subject: [PATCH 60/76] Port release notes from main to 5.1.7 (#3334) - Brought over all relevant 5.1 release notes from main to release/5.1. --- CHANGELOG.md | 36 +++++++++ release-notes/5.1/5.1.0-preview1.md | 5 ++ release-notes/5.1/5.1.0-preview2.md | 8 +- release-notes/5.1/5.1.0.md | 15 +++- release-notes/5.1/5.1.1.md | 70 +++++++++++++++++ release-notes/5.1/5.1.2.md | 78 +++++++++++++++++++ release-notes/5.1/5.1.3.md | 67 ++++++++++++++++ release-notes/5.1/5.1.4.md | 70 +++++++++++++++++ release-notes/5.1/5.1.5.md | 10 ++- release-notes/5.1/5.1.6.md | 75 ++++++++++++++++++ release-notes/5.1/5.1.7.md | 76 ++++++++++++++++++ release-notes/5.1/5.1.md | 19 ----- release-notes/5.1/README.md | 2 + release-notes/README.md | 7 +- .../AzureKeyVaultProvider/5.1/5.1.0.md | 72 +++++++++++++++++ .../AzureKeyVaultProvider/5.1/README.md | 7 ++ 16 files changed, 588 insertions(+), 29 deletions(-) create mode 100644 release-notes/5.1/5.1.1.md create mode 100644 release-notes/5.1/5.1.2.md create mode 100644 release-notes/5.1/5.1.3.md create mode 100644 release-notes/5.1/5.1.4.md create mode 100644 release-notes/5.1/5.1.6.md create mode 100644 release-notes/5.1/5.1.7.md delete mode 100644 release-notes/5.1/5.1.md create mode 100644 release-notes/add-ons/AzureKeyVaultProvider/5.1/5.1.0.md create mode 100644 release-notes/add-ons/AzureKeyVaultProvider/5.1/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index afcd9d2810..674ab87826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,42 @@ 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/) +# Release Notes + +## [Stable release 5.1.7] - 2025-04-25 + +This update brings the following changes since the 5.1.6 release: + +### Fixed + +- Fixed possible `NullPointerException` during socket receive (PR [#3285](https://github.com/dotnet/SqlClient/pull/3285)) +- Fixed inconsistencies between source and reference projects (PR [#3180](https://github.com/dotnet/SqlClient/pull/3180)) + +### Changed + +- Updated the following dependencies: + - [Microsoft.Data.SqlClient.SNI](https://www.nuget.org/packages/Microsoft.Data.SqlClient.SNI/5.1.2) 5.1.1 to 5.1.2 for .NET Framework on Windows (PR [#3294](https://github.com/dotnet/SqlClient/pull/3294)) + - [Microsoft.Data.SqlClient.SNI.runtime](https://www.nuget.org/packages/Microsoft.Data.SqlClient.SNI.runtime/5.1.2) 5.1.1 to 5.1.2 for .NET on Windows (PR [#3294](https://github.com/dotnet/SqlClient/pull/3294)) + - [Microsoft.Extensions.Caching.Memory](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory/6.0.3) 6.0.1 to 6.0.3 - Avoid [CVE-2024-43483](https://github.com/advisories/GHSA-qj66-m88j-hmgj) (PR [#3068](https://github.com/dotnet/SqlClient/pull/3068)) + - [Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting/6.0.1) 6.0.0 to 6.0.1 - Avoid transitive dependency on vulnerable [System.Text.Json](https://www.nuget.org/packages/System.Text.Json/6.0.0) 6.0.0 (PR [#3207](https://github.com/dotnet/SqlClient/pull/3207)) + - [System.Private.Uri](https://www.nuget.org/packages/System.Private.Uri) 4.3.2 - Avoid transitive [CVE-2019-0820](https://msrc.microsoft.com/update-guide/en-US/advisory/CVE-2019-0820) (PR [#3077](https://github.com/dotnet/SqlClient/pull/3077)) + - [System.Text.Encodings.Web](https://www.nuget.org/packages/System.Text.Encodings.Web/6.0.1) 6.0.0 to 6.0.1 - Avoid transitive downgrade for .NET Framework targets (PR [#3279](https://github.com/dotnet/SqlClient/pull/3279)) + - [System.Text.Json](https://www.nuget.org/packages/System.Text.Json/6.0.11) 6.0.11 - Avoid transitive dependencies on older vulnerable versions for .NET Framework targets (PR [#3279](https://github.com/dotnet/SqlClient/pull/3279)) + +## [Stable release 5.1.6] - 2024-08-27 + +### Fixed + +- Fixed Transient fault handling issue with `OpenAsync`. [#1983](https://github.com/dotnet/SqlClient/pull/1983) [#2508](https://github.com/dotnet/SqlClient/pull/2508) +- Fixed `AcquireTokenAsync` timeout handling for edge cases in `ActiveDirectoryAuthenticationProvider`. [#2706](https://github.com/dotnet/SqlClient/pull/2706) +- Fixed pending data with `SqlDataReader` against an encrypted column. [#2618](https://github.com/dotnet/SqlClient/pull/2618) [#2818](https://github.com/dotnet/SqlClient/pull/2818) + +### Changed + +- Upgraded `Azure.Identity` version from 1.11.3 to 1.11.4 [#2649] (https://github.com/dotnet/SqlClient/pull/2649) [#2529] (https://github.com/dotnet/SqlClient/pull/2529) to address [CVE-2024-35255](https://github.com/advisories/GHSA-m5vv-6r4h-3vj9). +- Upgraded `Microsoft.Identity.Client` version from 4.60.0 to 4.61.3 [#2649] (https://github.com/dotnet/SqlClient/pull/2649) [#2529] (https://github.com/dotnet/SqlClient/pull/2529) to address [CVE-2024-35255](https://github.com/advisories/GHSA-m5vv-6r4h-3vj9). +- Added caching to `TokenCredential` objects to take advantage of token caching. [#2776](https://github.com/dotnet/SqlClient/pull/2776) +- Code health improvements: [#2490] (https://github.com/dotnet/SqlClient/pull/2490) ## [Stable release 5.1.5] - 2024-01-29 diff --git a/release-notes/5.1/5.1.0-preview1.md b/release-notes/5.1/5.1.0-preview1.md index 222f10f7d5..9bee9f1288 100644 --- a/release-notes/5.1/5.1.0-preview1.md +++ b/release-notes/5.1/5.1.0-preview1.md @@ -4,6 +4,11 @@ This update brings the below changes over the previous release: +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. +- [Wraith2](https://github.com/Wraith2) +- [sorensenmatias](https://github.com/sorensenmatias) + ### Fixed - Fixed `ReadAsync()` behavior to register Cancellation token action before streaming results. [#1781](https://github.com/dotnet/SqlClient/pull/1781) diff --git a/release-notes/5.1/5.1.0-preview2.md b/release-notes/5.1/5.1.0-preview2.md index 93b58078a0..5bd4a240b0 100644 --- a/release-notes/5.1/5.1.0-preview2.md +++ b/release-notes/5.1/5.1.0-preview2.md @@ -4,6 +4,12 @@ This update brings the below changes over the previous release: +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. +- [Wraith2](https://github.com/Wraith2) +- [ErikEJ](https://github.com/ErikEJ) +- [panoskj](https://github.com/panoskj) + ### Breaking changes over preview release v5.1.0-preview1 - Add support for .NET 6.0 and Dropped support for .NET Core 3.1. [#1704](https://github.com/dotnet/SqlClient/pull/1704) [#1823](https://github.com/dotnet/SqlClient/pull/1823) @@ -36,7 +42,7 @@ The default value of the `ServerCertificate` connection setting is an empty stri ## Target Platform Support -- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Framework 4.6.2+ (Windows ARM64, Windows x86, Windows x64) - .NET 6.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) - .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) diff --git a/release-notes/5.1/5.1.0.md b/release-notes/5.1/5.1.0.md index 015b8966b9..2833f371f0 100644 --- a/release-notes/5.1/5.1.0.md +++ b/release-notes/5.1/5.1.0.md @@ -4,6 +4,15 @@ This update includes the following changes over the 5.0 release: +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. +- [Wraith2](https://github.com/Wraith2) +- [ErikEJ](https://github.com/ErikEJ) +- [roji](https://github.com/roji) +- [panoskj](https://github.com/panoskj) +- [teo-tsirpanis](https://github.com/teo-tsirpanis) +- [sorensenmatias](https://github.com/sorensenmatias) + ### Breaking changes - Dropped support for .NET Core 3.1. [#1704](https://github.com/dotnet/SqlClient/pull/1704) [#1823](https://github.com/dotnet/SqlClient/pull/1823) @@ -50,7 +59,7 @@ The default value of the `ServerCertificate` connection setting is an empty stri ## Target Platform Support -- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Framework 4.6.2+ (Windows ARM64, Windows x86, Windows x64) - .NET 6.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) - .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) @@ -70,7 +79,7 @@ The default value of the `ServerCertificate` connection setting is an empty stri #### .NET -- Microsoft.Data.SqlClient.SNI 5.1.0 +- Microsoft.Data.SqlClient.SNI.runtime 5.1.0 - Azure.Identity 1.7.0 - Microsoft.Identity.Client 4.47.2 - Microsoft.IdentityModel.JsonWebTokens 6.24.0 @@ -86,7 +95,7 @@ The default value of the `ServerCertificate` connection setting is an empty stri #### .NET Standard -- Microsoft.Data.SqlClient.SNI 5.1.0 +- Microsoft.Data.SqlClient.SNI.runtime 5.1.0 - Azure.Identity 1.7.0 - Microsoft.Identity.Client 4.47.2 - Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 diff --git a/release-notes/5.1/5.1.1.md b/release-notes/5.1/5.1.1.md new file mode 100644 index 0000000000..fea3f68033 --- /dev/null +++ b/release-notes/5.1/5.1.1.md @@ -0,0 +1,70 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.1 released 28 March 2023 + +This update includes the following changes over the previous release: + +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. + +### Fixed + +- Fixed an incorrect exception when a symmetric key fails to decrypt a column using Always Encrypted. [#1968](https://github.com/dotnet/SqlClient/pull/1968) +- Fixed `TransactionScope` connection issue when `Enlist` is `enabled`, `Pooling` is `disabled`, and `Network Connection Type` is set to `Redirect`. [#1967](https://github.com/dotnet/SqlClient/pull/1967) +- Fixed throttling of token requests by calling `AcquireTokenSilent`. [#1966](https://github.com/dotnet/SqlClient/pull/1966) +- Fixed TDS RPC error on large queries in `SqlCommand.ExecuteReaderAsync`. [#1965](https://github.com/dotnet/SqlClient/pull/1965) +- Fixed `NullReferenceException` in `GetBytesAsync`. [#1964](https://github.com/dotnet/SqlClient/pull/1964) + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows ARM64, Windows x86, Windows x64) +- .NET 6.0+ (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 5.1.0 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Text.Encoding.Web 6.0.0 + +#### .NET + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.0 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.0 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.2.md b/release-notes/5.1/5.1.2.md new file mode 100644 index 0000000000..20a71f34ab --- /dev/null +++ b/release-notes/5.1/5.1.2.md @@ -0,0 +1,78 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.2 released 26 October 2023 + +This update includes the following changes over the previous release: + +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. +- [emidah](https://github.com/emidah) + +### Fixed + +- Fixed access violation when using SQL Express user instance. [#2101](https://github.com/dotnet/SqlClient/pull/2101) +- Fixed Always Encrypted secure enclave retry logic for async queries. [#1988](https://github.com/dotnet/SqlClient/pull/1988) +- Fixed LocalDb and managed SNI by improving the error messages and avoid falling back to the local service. [#2129](https://github.com/dotnet/SqlClient/pull/2129) +- Fixed .NET and .NET Standard file version. [2093](https://github.com/dotnet/SqlClient/pull/2093) +- Fixed non-string values and `SqlConnectionStringBuilder` property indexer issue. [#2018](https://github.com/dotnet/SqlClient/pull/2018) +- Fixed `SqlConnectionEncryptOption` type conversion by introducing the `SqlConnectionEncryptOptionConverter` attribute when using **appsettings.json** files. [#2057](https://github.com/dotnet/SqlClient/pull/2057) +- Fixed Transient fault handling issue with `OpenAsync`. [#1983](https://github.com/dotnet/SqlClient/pull/1983) +- Fixed activity correlator to continue use of same GUID for connection activity. [#1997](https://github.com/dotnet/SqlClient/pull/1997) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.1.1`. [#2123](https://github.com/dotnet/SqlClient/pull/2123) + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows ARM64, Windows x86, Windows x64) +- .NET 6.0+ (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 5.1.1 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Text.Encoding.Web 6.0.0 + +#### .NET + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.3.md b/release-notes/5.1/5.1.3.md new file mode 100644 index 0000000000..f96e347212 --- /dev/null +++ b/release-notes/5.1/5.1.3.md @@ -0,0 +1,67 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.3 released 9 January 2024 + +This update includes the following changes over the previous release: + +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. + +### Fixed + +- Fixed encryption downgrade issue. [CVE-2024-0056](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-0056) +- Fixed certificate chain validation logic flow. + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows ARM64, Windows x86, Windows x64) +- .NET 6.0+ (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 5.1.1 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Text.Encoding.Web 6.0.0 + +#### .NET + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.4.md b/release-notes/5.1/5.1.4.md new file mode 100644 index 0000000000..62c325750e --- /dev/null +++ b/release-notes/5.1/5.1.4.md @@ -0,0 +1,70 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.4 released 9 January 2024 + +This update includes the following changes over the previous release: + +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. + +### Fixed + +- Fixed a deadlock problem for distributed transactions when on .NET. + +### Changed + +- Upgraded `Azure.Identity` dependency version to [1.10.3](https://www.nuget.org/packages/Azure.Identity/1.10.3) to address [CVE-2023-36414](https://github.com/advisories/GHSA-5mfx-4wcx-rv27). + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows ARM64, Windows x86, Windows x64) +- .NET 6.0+ (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 5.1.1 +- Azure.Identity 1.10.3 +- Microsoft.Identity.Client 4.56.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Text.Encoding.Web 6.0.0 + +#### .NET + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 +- Azure.Identity 1.10.3 +- Microsoft.Identity.Client 4.56.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 +- Azure.Identity 1.10.3 +- Microsoft.Identity.Client 4.56.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.5.md b/release-notes/5.1/5.1.5.md index a272ffa527..540061f8f1 100644 --- a/release-notes/5.1/5.1.5.md +++ b/release-notes/5.1/5.1.5.md @@ -4,6 +4,10 @@ This update includes the following changes over the previous release: +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. +- [ErikEJ](https://github.com/ErikEJ) + ### Fixed - Fixed connection to unsubscribe from transaction completion events before returning it to the connection pool [#2321](https://github.com/dotnet/SqlClient/pull/2321) @@ -15,7 +19,7 @@ This update includes the following changes over the previous release: ## Target Platform Support -- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Framework 4.6.2+ (Windows ARM64, Windows x86, Windows x64) - .NET 6.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) - .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) @@ -35,7 +39,7 @@ This update includes the following changes over the previous release: #### .NET -- Microsoft.Data.SqlClient.SNI 5.1.1 +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 - Azure.Identity 1.10.3 - Microsoft.Identity.Client 4.56.2 - Microsoft.IdentityModel.JsonWebTokens 6.35.0 @@ -51,7 +55,7 @@ This update includes the following changes over the previous release: #### .NET Standard -- Microsoft.Data.SqlClient.SNI 5.1.1 +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 - Azure.Identity 1.10.3 - Microsoft.Identity.Client 4.56.2 - Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 diff --git a/release-notes/5.1/5.1.6.md b/release-notes/5.1/5.1.6.md new file mode 100644 index 0000000000..3c6cb9ebe6 --- /dev/null +++ b/release-notes/5.1/5.1.6.md @@ -0,0 +1,75 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.6 released 27 August 2024 + +This update includes the following changes over the previous release: + +### Contributors +Thanks to the following public contributors. Their efforts toward this project are very much appreciated. + +### Fixed + +- Fixed Transient fault handling issue with `OpenAsync`. [#1983](https://github.com/dotnet/SqlClient/pull/1983) [#2508](https://github.com/dotnet/SqlClient/pull/2508) +- Fixed `AcquireTokenAsync` timeout handling for edge cases in `ActiveDirectoryAuthenticationProvider`. [#2706](https://github.com/dotnet/SqlClient/pull/2706) +- Fixed pending data with `SqlDataReader` against an encrypted column. [#2618](https://github.com/dotnet/SqlClient/pull/2618) [#2818](https://github.com/dotnet/SqlClient/pull/2818) + +### Changed + +- Upgraded `Azure.Identity` version from 1.11.3 to 1.11.4 [#2649] (https://github.com/dotnet/SqlClient/pull/2649) [#2529] (https://github.com/dotnet/SqlClient/pull/2529) to address [CVE-2024-35255](https://github.com/advisories/GHSA-m5vv-6r4h-3vj9). +- Upgraded `Microsoft.Identity.Client` version from 4.60.0 to 4.61.3 [#2649] (https://github.com/dotnet/SqlClient/pull/2649) [#2529] (https://github.com/dotnet/SqlClient/pull/2529) to address [CVE-2024-35255](https://github.com/advisories/GHSA-m5vv-6r4h-3vj9). +- Added caching to `TokenCredential` objects to take advantage of token caching. [#2776](https://github.com/dotnet/SqlClient/pull/2776) +- Code health improvements: [#2490] (https://github.com/dotnet/SqlClient/pull/2490) + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows ARM64, Windows x86, Windows x64) +- .NET 6.0+ (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 5.1.1 +- Azure.Identity 1.11.4 +- Microsoft.Identity.Client 4.61.3 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Text.Encodings.Web 6.0.0 + +#### .NET + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 +- Azure.Identity 1.11.4 +- Microsoft.Identity.Client 4.61.3 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.1.1 +- Azure.Identity 1.11.4 +- Microsoft.Identity.Client 4.61.3 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.7.md b/release-notes/5.1/5.1.7.md new file mode 100644 index 0000000000..f8716c7b21 --- /dev/null +++ b/release-notes/5.1/5.1.7.md @@ -0,0 +1,76 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.7 - April 25, 2025 + +This update brings the following changes since the 5.1.6 release: + +### Fixed + +- Fixed possible `NullPointerException` during socket receive (PR [#3285](https://github.com/dotnet/SqlClient/pull/3285)) +- Fixed inconsistencies between source and reference projects (PR [#3180](https://github.com/dotnet/SqlClient/pull/3180)) + +### Changed + +- Updated the following dependencies: + - [Microsoft.Data.SqlClient.SNI](https://www.nuget.org/packages/Microsoft.Data.SqlClient.SNI/5.1.2) 5.1.1 to 5.1.2 for .NET Framework on Windows (PR [#3294](https://github.com/dotnet/SqlClient/pull/3294)) + - [Microsoft.Data.SqlClient.SNI.runtime](https://www.nuget.org/packages/Microsoft.Data.SqlClient.SNI.runtime/5.1.2) 5.1.1 to 5.1.2 for .NET on Windows (PR [#3294](https://github.com/dotnet/SqlClient/pull/3294)) + - [Microsoft.Extensions.Caching.Memory](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory/6.0.3) 6.0.1 to 6.0.3 - Avoid [CVE-2024-43483](https://github.com/advisories/GHSA-qj66-m88j-hmgj) (PR [#3068](https://github.com/dotnet/SqlClient/pull/3068)) + - [Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting/6.0.1) 6.0.0 to 6.0.1 - Avoid transitive dependency on vulnerable [System.Text.Json](https://www.nuget.org/packages/System.Text.Json/6.0.0) 6.0.0 (PR [#3207](https://github.com/dotnet/SqlClient/pull/3207)) + - [System.Private.Uri](https://www.nuget.org/packages/System.Private.Uri) 4.3.2 - Avoid transitive [CVE-2019-0820](https://msrc.microsoft.com/update-guide/en-US/advisory/CVE-2019-0820) (PR [#3077](https://github.com/dotnet/SqlClient/pull/3077)) + - [System.Text.Encodings.Web](https://www.nuget.org/packages/System.Text.Encodings.Web/6.0.1) 6.0.0 to 6.0.1 - Avoid transitive downgrade for .NET Framework targets (PR [#3279](https://github.com/dotnet/SqlClient/pull/3279)) + - [System.Text.Json](https://www.nuget.org/packages/System.Text.Json/6.0.11) 6.0.11 - Avoid transitive dependencies on older vulnerable versions for .NET Framework targets (PR [#3279](https://github.com/dotnet/SqlClient/pull/3279)) + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET 6.0+ (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 + +- Azure.Identity 1.11.4 +- Microsoft.Data.SqlClient.SNI 5.1.2 +- Microsoft.Identity.Client 4.61.3 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Text.Encoding.Web 6.0.1 +- System.Text.Json 6.0.11 + +### .NET + +- Azure.Identity 1.11.4 +- Microsoft.Data.SqlClient.SNI 5.1.2 +- Microsoft.Identity.Client 4.61.3 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.1 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +### .NET Standard + +- Azure.Identity 1.11.4 +- Microsoft.Data.SqlClient.SNI 5.1.2 +- Microsoft.Identity.Client 4.61.3 +- Microsoft.IdentityModel.JsonWebTokens 6.35.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.35.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.1 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.md b/release-notes/5.1/5.1.md deleted file mode 100644 index 09b83805c2..0000000000 --- a/release-notes/5.1/5.1.md +++ /dev/null @@ -1,19 +0,0 @@ -# Microsoft.Data.SqlClient 5.1 Releases - -The following Microsoft.Data.SqlClient 5.1 stable releases have been shipped: - -| Release Date | Version | Notes | -| :-- | :-- | :--: | -| 2024/01/29 | 5.1.5 | [release notes](5.1.5.md) | -| 2024/01/09 | 5.1.4 | [release notes](5.1.4.md) | -| 2024/01/09 | 5.1.3 | [release notes](5.1.3.md) | -| 2023/10/26 | 5.1.2 | [release notes](5.1.2.md) | -| 2023/03/28 | 5.1.1 | [release notes](5.1.1.md) | -| 2023/01/19 | 5.1.0 | [release notes](5.1.0.md) | - -The following Microsoft.Data.SqlClient 5.1 preview releases have been shipped: - -| Release Date | Version | Notes | -| :-- | :-- | :--: | -| 2022/11/10 | 5.1.0-preview2.22314.2 | [release notes](5.1.0-preview2.md) | -| 2022/10/19 | 5.1.0-preview1.22279.3 | [release notes](5.1.0-preview1.md) | diff --git a/release-notes/5.1/README.md b/release-notes/5.1/README.md index 09b83805c2..27ed889b2a 100644 --- a/release-notes/5.1/README.md +++ b/release-notes/5.1/README.md @@ -4,6 +4,8 @@ The following Microsoft.Data.SqlClient 5.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2025/04/25 | 5.1.7 | [release notes](5.1.7.md) | +| 2024/08/27 | 5.1.6 | [release notes](5.1.6.md) | | 2024/01/29 | 5.1.5 | [release notes](5.1.5.md) | | 2024/01/09 | 5.1.4 | [release notes](5.1.4.md) | | 2024/01/09 | 5.1.3 | [release notes](5.1.3.md) | diff --git a/release-notes/README.md b/release-notes/README.md index ba76fe8433..2fbfc1de70 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 5.0](5.0). +The latest stable release is [Microsoft.Data.SqlClient 5.1](5.1). ## Release Information @@ -8,8 +8,8 @@ The latest stable release is [Microsoft.Data.SqlClient 5.0](5.0). - [Microsoft.Data.SqlClient 5.0](5.0) - [Microsoft.Data.SqlClient 4.1](4.1) - [Microsoft.Data.SqlClient 4.0](4.0) -- [Microsoft.Data.SqlClient 3.0](3.0) - [Microsoft.Data.SqlClient 3.1](3.1) +- [Microsoft.Data.SqlClient 3.0](3.0) - [Microsoft.Data.SqlClient 2.1](2.1) - [Microsoft.Data.SqlClient 2.0](2.0) - [Microsoft.Data.SqlClient 1.1](1.1) @@ -17,10 +17,11 @@ The latest stable release is [Microsoft.Data.SqlClient 5.0](5.0). # Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider Release Notes -The latest stable release is [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0](add-ons/AzureKeyVaultProvider/3.0). +The latest stable release is [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 5.1](add-ons/AzureKeyVaultProvider/5.1). ## Release Information +- [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 5.1](add-ons/AzureKeyVaultProvider/5.1) - [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) diff --git a/release-notes/add-ons/AzureKeyVaultProvider/5.1/5.1.0.md b/release-notes/add-ons/AzureKeyVaultProvider/5.1/5.1.0.md new file mode 100644 index 0000000000..159a1072d5 --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/5.1/5.1.0.md @@ -0,0 +1,72 @@ +# Release Notes + +## General Availability of Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider + +_**5.1.0 released 01 February 2024**_ + +This library contains the implementation of `Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider` for accessing Azure Key Vault, and the provider class is named `SqlColumnEncryptionAzureKeyVaultProvider`. + +### Changed + +- Changed Microsoft.Data.SqlClient version 3.0.0 to 5.1.5 [#2330](https://github.com/dotnet/SqlClient/pull/2330) +- Changed Azure.Core version 1.6.0 to 1.35.0 [#2330](https://github.com/dotnet/SqlClient/pull/2330) +- Changed Azure.Security.KeyVault.Keys 4.0.3 to 4.5.0 [#2330](https://github.com/dotnet/SqlClient/pull/2330) +- Changed Microsoft.Extensions.Caching.Memory 5.0.0 to 8.0.0 for .Net 8.0 and 6.0.1 for other Target frameworks [#2330](https://github.com/dotnet/SqlClient/pull/2330) + +### Working with SQLColumnEncryptionAzureKeyVaultProvider + +`SqlColumnEncryptionAzureKeyVaultProvider` **v5.1** is implemented against `Microsoft.Data.SqlClient` **v5.1** and supports .NET Framework 4.6.2+, .NET Core 6.0+, 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-5.1) +- [SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection](https://docs.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlconnection.registercolumnencryptionkeystoreprovidersonconnection?view=sqlclient-dotnet-5.1) (Added in version 3.0.0) +- [SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand](https://docs.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlcommand.registercolumnencryptionkeystoreprovidersoncommand?view=sqlclient-dotnet-5.1) (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.2+ +- .NET Core 6.0+ (Windows x86, Windows x64, Linux, macOS) +- .NET Standard 2.0+ + +### Dependencies + +#### .NET Framework + +- Azure.Core 1.35.0 +- Azure.Security.KeyVault.Keys 4.5.0 +- Microsoft.Data.SqlClient 5.1.5 +- Microsoft.Extensions.Caching.Memory 6.0.1 + +##### .NET 6 + +- Azure.Core 1.35.0 +- Azure.Security.KeyVault.Keys 4.5.0 +- Microsoft.Data.SqlClient 5.1.5 +- Microsoft.Extensions.Caching.Memory 6.0.1 + +#### .NET 8 + +- Azure.Core 1.35.0 +- Azure.Security.KeyVault.Keys 4.5.0 +- Microsoft.Data.SqlClient 5.1.5 +- Microsoft.Extensions.Caching.Memory 8.0.0 + +#### .NET Standard + +- Azure.Core 1.35.0 +- Azure.Security.KeyVault.Keys 4.5.0 +- Microsoft.Data.SqlClient 5.1.5 +- Microsoft.Extensions.Caching.Memory 6.0.1 diff --git a/release-notes/add-ons/AzureKeyVaultProvider/5.1/README.md b/release-notes/add-ons/AzureKeyVaultProvider/5.1/README.md new file mode 100644 index 0000000000..55d9d38bc4 --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/5.1/README.md @@ -0,0 +1,7 @@ +# Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 5.1 Releases + +The following Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 5.1 stable releases have been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2024/02/01 | 5.1.0 | [release notes](5.1.0.md) | From 6af24bce88bd7455a3a5dcb7a810fb55544628ad Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Wed, 4 Jun 2025 22:06:34 -0500 Subject: [PATCH 61/76] 5.1 | APIScan | MSAL `WithClientName` (3rd attempt) (#3367) * Backport APIScan changes to v5.1 release branch (second attempt with less mistakes) * Add missing NETSTANDARD stuff --- .../ActiveDirectoryAuthenticationProvider.cs | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) 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 f2fd1aaceb..73fce50774 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -540,45 +540,30 @@ private static bool AreEqual(byte[] a1, byte[] a2) private IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publicClientAppKey) { - IPublicClientApplication publicClientApplication; - -#if NETSTANDARD - if (_parentActivityOrWindowFunc != null) - { - publicClientApplication = PublicClientApplicationBuilder.Create(publicClientAppKey._applicationClientId) - .WithAuthority(publicClientAppKey._authority) - .WithClientName(Common.DbConnectionStringDefaults.ApplicationName) - .WithClientVersion(Common.ADP.GetAssemblyVersion().ToString()) - .WithRedirectUri(publicClientAppKey._redirectUri) - .WithParentActivityOrWindow(_parentActivityOrWindowFunc) - .Build(); - } -#endif -#if NETFRAMEWORK - if (_iWin32WindowFunc != null) + PublicClientApplicationBuilder builder = PublicClientApplicationBuilder + .CreateWithApplicationOptions(new PublicClientApplicationOptions + { + ClientId = publicClientAppKey._applicationClientId, + ClientName = Common.DbConnectionStringDefaults.ApplicationName, + ClientVersion = Common.ADP.GetAssemblyVersion().ToString(), + RedirectUri = publicClientAppKey._redirectUri, + }) + .WithAuthority(publicClientAppKey._authority); + + #if NETFRAMEWORK + if (_iWin32WindowFunc is not null) { - publicClientApplication = PublicClientApplicationBuilder.Create(publicClientAppKey._applicationClientId) - .WithAuthority(publicClientAppKey._authority) - .WithClientName(Common.DbConnectionStringDefaults.ApplicationName) - .WithClientVersion(Common.ADP.GetAssemblyVersion().ToString()) - .WithRedirectUri(publicClientAppKey._redirectUri) - .WithParentActivityOrWindow(_iWin32WindowFunc) - .Build(); + builder.WithParentActivityOrWindow(_iWin32WindowFunc); } -#endif -#if !NETCOREAPP - else -#endif + #endif + #if NETSTANDARD + if (_parentActivityOrWindowFunc is not null) { - publicClientApplication = PublicClientApplicationBuilder.Create(publicClientAppKey._applicationClientId) - .WithAuthority(publicClientAppKey._authority) - .WithClientName(Common.DbConnectionStringDefaults.ApplicationName) - .WithClientVersion(Common.ADP.GetAssemblyVersion().ToString()) - .WithRedirectUri(publicClientAppKey._redirectUri) - .Build(); + builder.WithParentActivityOrWindow(_parentActivityOrWindowFunc); } + #endif - return publicClientApplication; + return builder.Build(); } private static TokenCredentialData CreateTokenCredentialInstance(TokenCredentialKey tokenCredentialKey, string secret) From 82ce7cf7d4b4a7b75f46ac55b0ee842c6b333f48 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:30:46 -0300 Subject: [PATCH 62/76] User Story 38481: Fix unique db object name issues - Fixed the unique name generators to: - Keep max lengths to 30 and 96 characters respectively. - Ensure uniqueness at the start of the names. - Added link to database identifier syntax. --- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 10 +- .../AlwaysEncrypted/CspProviderExt.cs | 49 ----- .../ManualTests/DataCommon/DataTestUtility.cs | 202 ++++++++++++++---- .../ProviderAgnostic/ReaderTest/ReaderTest.cs | 6 +- .../SQL/AdapterTest/AdapterTest.cs | 10 +- .../SQL/ConnectivityTests/ConnectivityTest.cs | 14 +- .../DataClassificationTest.cs | 6 +- .../SQL/DataReaderTest/DataReaderTest.cs | 8 +- .../SQL/DataStreamTest/DataStreamTest.cs | 10 +- .../SQL/ParameterTest/DateTimeVariantTest.cs | 52 ++--- .../SQL/ParameterTest/ParametersTest.cs | 141 +++++++++++- .../ParameterTest/SqlAdapterUpdateBatch.cs | 2 +- .../SQL/ParameterTest/SqlVariantParam.cs | 4 +- .../RetryLogic/SqlCommandReliabilityTest.cs | 4 +- .../SqlConnectionReliabilityTest.cs | 2 +- .../AdjustPrecScaleForBulkCopy.cs | 2 +- .../AzureDistributedTransaction.cs | 2 +- .../CopyWidenNullInexactNumerics.cs | 4 +- .../DataConversionErrorMessageTest.cs | 2 +- .../SQL/SqlCommand/SqlCommandCompletedTest.cs | 2 +- .../SQL/SqlCommand/SqlCommandSetTest.cs | 4 +- .../SqlFileStreamTest/SqlFileStreamTest.cs | 4 +- .../SQL/UdtTest/SqlServerTypesTest.cs | 2 +- .../SQL/UdtTest/UdtBulkCopyTest.cs | 6 +- .../tests/ManualTests/SQL/UdtTest/UdtTest2.cs | 16 +- .../SQL/Utf8SupportTest/Utf8SupportTest.cs | 2 +- 26 files changed, 378 insertions(+), 188 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 8e7458f02e..b987bdbc99 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -148,8 +148,8 @@ public void SqlParameterProperties(string connection) const string firstColumnName = @"firstColumn"; const string secondColumnName = @"secondColumn"; const string thirdColumnName = @"thirdColumn"; - string inputProcedureName = DataTestUtility.GetUniqueName("InputProc").ToString(); - string outputProcedureName = DataTestUtility.GetUniqueName("OutputProc").ToString(); + string inputProcedureName = DataTestUtility.GetShortName("InputProc").ToString(); + string outputProcedureName = DataTestUtility.GetShortName("OutputProc").ToString(); const int charColumnSize = 100; const int decimalColumnPrecision = 10; const int decimalColumnScale = 4; @@ -694,7 +694,7 @@ public void TestExecuteReader(string connection) [ClassData(typeof(AEConnectionStringProvider))] public async void TestExecuteReaderAsyncWithLargeQuery(string connectionString) { - string randomName = DataTestUtility.GetUniqueName(Guid.NewGuid().ToString().Replace("-", ""), false); + string randomName = DataTestUtility.GetShortName(Guid.NewGuid().ToString().Replace("-", ""), false); if (randomName.Length > 50) { randomName = randomName.Substring(0, 50); @@ -878,8 +878,8 @@ public void TestEnclaveStoredProceduresWithAndWithoutParameters(string connectio using SqlCommand sqlCommand = new("", sqlConnection, transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled); - string procWithoutParams = DataTestUtility.GetUniqueName("EnclaveWithoutParams", withBracket: false); - string procWithParam = DataTestUtility.GetUniqueName("EnclaveWithParams", withBracket: false); + string procWithoutParams = DataTestUtility.GetShortName("EnclaveWithoutParams", withBracket: false); + string procWithParam = DataTestUtility.GetShortName("EnclaveWithParams", withBracket: false); try { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs index 49071def2e..e26c67382e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs @@ -157,55 +157,6 @@ public void TestRoundTripWithCSPAndCertStoreProvider() } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] - [ClassData(typeof(AEConnectionStringProvider))] - public void TestEncryptDecryptWithCSP(string connectionString) - { - string providerName = @"Microsoft Enhanced RSA and AES Cryptographic Provider"; - string keyIdentifier = DataTestUtility.GetUniqueNameForSqlServer("CSP"); - - try - { - CertificateUtilityWin.RSAPersistKeyInCsp(providerName, keyIdentifier); - string cspPath = String.Concat(providerName, @"/", keyIdentifier); - - SQLSetupStrategyCspExt sqlSetupStrategyCsp = new SQLSetupStrategyCspExt(cspPath); - string tableName = sqlSetupStrategyCsp.CspProviderTable.Name; - - try - { - using SqlConnection sqlConn = new(connectionString); - sqlConn.Open(); - - Table.DeleteData(tableName, sqlConn); - - // insert 1 row data - Customer customer = new Customer(45, "Microsoft", "Corporation"); - - DatabaseHelper.InsertCustomerData(sqlConn, null, tableName, customer); - - // Test INPUT parameter on an encrypted parameter - using SqlCommand sqlCommand = new(@$"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @firstName", - sqlConn, null, SqlCommandColumnEncryptionSetting.Enabled); - SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); - Console.WriteLine(@"Exception: {0}"); - customerFirstParam.Direction = System.Data.ParameterDirection.Input; - - using SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(); - ValidateResultSet(sqlDataReader); - } - finally - { - // clean up database resources - sqlSetupStrategyCsp.Dispose(); - } - } - finally - { - CertificateUtilityWin.RSADeleteKeyInCsp(providerName, keyIdentifier); - } - } - /// /// Validates that the results are the ones expected. /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 1375cbea2f..2723853f27 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -88,7 +88,7 @@ public static bool TcpConnectionStringDoesNotUseAadAuth { get { - SqlConnectionStringBuilder builder = new (TCPConnectionString); + SqlConnectionStringBuilder builder = new(TCPConnectionString); return builder.Authentication == SqlAuthenticationMethod.SqlPassword || builder.Authentication == SqlAuthenticationMethod.NotSpecified; } } @@ -415,48 +415,176 @@ public static bool DoesHostAddressContainBothIPv4AndIPv6() } } + // Generate a new GUID and return the characters from its 1st and 4th + // parts, as shown here: + // + // 7ff01cb8-88c7-11f0-b433-00155d7e531e + // ^^^^^^^^ ^^^^ + // + // These 12 characters are concatenated together without any + // separators. These 2 parts typically comprise a timestamp and clock + // sequence, most likely to be unique for tests that generate names in + // quick succession. + private static string GetGuidParts() + { + var guid = Guid.NewGuid().ToString(); + // GOTCHA: The slice operator is inclusive of the start index and + // exclusive of the end index! + return guid.Substring(0, 8) + guid.Substring(19, 4); + } + /// - /// Generate a unique name to use in Sql Server; - /// some providers does not support names (Oracle supports up to 30). + /// Generate a short unique database object name, whose maximum length + /// is 30 characters, with the format: + /// + /// _ + /// + /// The Prefix will be truncated to satisfy the overall maximum length. + /// + /// The GUID parts will be the characters from the 1st and 4th blocks + /// from a traditional string representation, as shown here: + /// + /// 7ff01cb8-88c7-11f0-b433-00155d7e531e + /// ^^^^^^^^ ^^^^ + /// + /// These 2 parts typically comprise a timestamp and clock sequence, + /// most likely to be unique for tests that generate names in quick + /// succession. The 12 characters are concatenated together without any + /// separators. /// - /// The name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length). - /// Name without brackets. - /// Unique name by considering the Sql Server naming rules. - public static string GetUniqueName(string prefix, bool withBracket = true) - { - string escapeLeft = withBracket ? "[" : string.Empty; - string escapeRight = withBracket ? "]" : string.Empty; - string uniqueName = string.Format("{0}{1}_{2}_{3}{4}", - escapeLeft, - prefix, - DateTime.Now.Ticks.ToString("X", CultureInfo.InvariantCulture), // up to 8 characters - Guid.NewGuid().ToString().Substring(0, 6), // take the first 6 characters only - escapeRight); - return uniqueName; + /// + /// + /// The prefix to use when generating the unique name, truncated to at + /// most 18 characters when withBracket is false, and 16 characters when + /// withBracket is true. + /// + /// This should not contain any characters that cannot be used in + /// database object names. See: + /// + /// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers + /// + /// + /// + /// When true, the entire generated name will be enclosed in square + /// brackets, for example: + /// + /// [MyPrefix_7ff01cb811f0] + /// + /// + /// + /// A unique database object name, no more than 30 characters long. + /// + public static string GetShortName(string prefix, bool withBracket = true) + { + StringBuilder name = new(30); + + if (withBracket) + { + name.Append('['); + } + + int maxPrefixLength = withBracket ? 16 : 18; + if (prefix.Length > maxPrefixLength) + { + prefix = prefix.Substring(0, maxPrefixLength); + } + + name.Append(prefix); + name.Append('_'); + name.Append(GetGuidParts()); + + if (withBracket) + { + name.Append(']'); + } + + return name.ToString(); } /// - /// Uses environment values `UserName` and `MachineName` in addition to the specified `prefix` and current date - /// to generate a unique name to use in Sql Server; - /// SQL Server supports long names (up to 128 characters), add extra info for troubleshooting. + /// Generate a long unique database object name, whose maximum length is + /// 96 characters, with the format: + /// + /// ___ + /// + /// The Prefix will be truncated to satisfy the overall maximum length. + /// + /// The GUID Parts will be the characters from the 1st and 4th blocks + /// from a traditional string representation, as shown here: + /// + /// 7ff01cb8-88c7-11f0-b433-00155d7e531e + /// ^^^^^^^^ ^^^^ + /// + /// These 2 parts typically comprise a timestamp and clock sequence, + /// most likely to be unique for tests that generate names in quick + /// succession. The 12 characters are concatenated together without any + /// separators. + /// + /// The UserName and MachineName are obtained from the Environment, + /// and will be truncated to satisfy the maximum overall length. /// - /// Add the prefix to the generate string. - /// Database name must be pass with brackets by default. - /// Unique name by considering the Sql Server naming rules. - public static string GetUniqueNameForSqlServer(string prefix, bool withBracket = true) - { - string extendedPrefix = string.Format( - "{0}_{1}_{2}@{3}", - prefix, - Environment.UserName, - Environment.MachineName, - DateTime.Now.ToString("yyyy_MM_dd", CultureInfo.InvariantCulture)); - string name = GetUniqueName(extendedPrefix, withBracket); - if (name.Length > 128) - { - throw new ArgumentOutOfRangeException("the name is too long - SQL Server names are limited to 128"); - } - return name; + /// + /// + /// The prefix to use when generating the unique name, truncated to at + /// most 32 characters. + /// + /// This should not contain any characters that cannot be used in + /// database object names. See: + /// + /// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers + /// + /// + /// + /// When true, the entire generated name will be enclosed in square + /// brackets, for example: + /// + /// [MyPrefix_7ff01cb811f0_test_user_ci_agent_machine_name] + /// + /// + /// + /// A unique database object name, no more than 96 characters long. + /// + public static string GetLongName(string prefix, bool withBracket = true) + { + StringBuilder name = new(96); + + if (withBracket) + { + name.Append('['); + } + + if (prefix.Length > 32) + { + prefix = prefix.Substring(0, 32); + } + + name.Append(prefix); + name.Append('_'); + name.Append(GetGuidParts()); + name.Append('_'); + + var suffix = + Environment.UserName + '_' + + Environment.MachineName; + + int maxSuffixLength = 96 - name.Length; + if (withBracket) + { + --maxSuffixLength; + } + if (suffix.Length > maxSuffixLength) + { + suffix = suffix.Substring(0, maxSuffixLength); + } + + name.Append(suffix); + + if (withBracket) + { + name.Append(']'); + } + + return name.ToString(); } public static bool IsSupportingDistributedTransactions() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs index 5d09be77f4..5728a8b1ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs @@ -19,7 +19,7 @@ public static void TestMain() { string connectionString = DataTestUtility.TCPConnectionString; - string tempTable = DataTestUtility.GetUniqueNameForSqlServer("table"); + string tempTable = DataTestUtility.GetLongName("table"); DbProviderFactory provider = SqlClientFactory.Instance; try @@ -275,7 +275,7 @@ public static void TestMain() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void SqlDataReader_SqlBuffer_GetFieldValue() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBuffer_GetFieldValue"); + string tableName = DataTestUtility.GetLongName("SqlBuffer_GetFieldValue"); DateTimeOffset dtoffset = DateTimeOffset.Now; DateTime dt = DateTime.Now; //Exclude the millisecond because of rounding at some points by SQL Server. @@ -374,7 +374,7 @@ public static void SqlDataReader_SqlBuffer_GetFieldValue() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async Task SqlDataReader_SqlBuffer_GetFieldValue_Async() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBuffer_GetFieldValue_Async"); + string tableName = DataTestUtility.GetLongName("SqlBuffer_GetFieldValue_Async"); DateTimeOffset dtoffset = DateTimeOffset.Now; DateTime dt = DateTime.Now; //Exclude the millisecond because of rounding at some points by SQL Server. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs index 9d43567cf1..371550e105 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs @@ -54,7 +54,7 @@ public class AdapterTest public AdapterTest() { // create random name for temp tables - _tempTable = DataTestUtility.GetUniqueName("AdapterTest"); + _tempTable = DataTestUtility.GetShortName("AdapterTest"); _tempTable = _tempTable.Replace('-', '_'); _randomGuid = Guid.NewGuid().ToString(); @@ -487,7 +487,7 @@ public void ParameterTest_AllTypes() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void ParameterTest_InOut() { - string procName = DataTestUtility.GetUniqueName("P"); + string procName = DataTestUtility.GetShortName("P"); // input, output string spCreateInOut = "CREATE PROCEDURE " + procName + " @in int, @inout int OUTPUT, @out nvarchar(8) OUTPUT " + @@ -768,13 +768,13 @@ public void BulkUpdateTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void UpdateRefreshTest() { - string identTableName = DataTestUtility.GetUniqueName("ID_"); + string identTableName = DataTestUtility.GetShortName("ID_"); string createIdentTable = $"CREATE TABLE {identTableName} (id int IDENTITY," + "LastName nvarchar(50) NULL," + "Firstname nvarchar(50) NULL)"; - string spName = DataTestUtility.GetUniqueName("sp_insert", withBracket: false); + string spName = DataTestUtility.GetShortName("sp_insert", withBracket: false); string spCreateInsert = $"CREATE PROCEDURE {spName}" + "(@FirstName nvarchar(50), @LastName nvarchar(50), @id int OUTPUT) " + @@ -1087,7 +1087,7 @@ public void AutoGenUpdateTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void AutoGenErrorTest() { - string identTableName = DataTestUtility.GetUniqueName("ID_"); + string identTableName = DataTestUtility.GetShortName("ID_"); string createIdentTable = $"CREATE TABLE {identTableName} (id int IDENTITY," + "LastName nvarchar(50) 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 7675643382..dfdbf5229f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -368,9 +368,10 @@ public static void ConnectionOpenDisableRetry() { SqlConnectionStringBuilder connectionStringBuilder = new(DataTestUtility.TCPConnectionString) { - InitialCatalog = "DoesNotExist0982532435423", + InitialCatalog = DataTestUtility.GetLongName("DoesNotExist", false), Pooling = false, - ConnectTimeout=15 + ConnectTimeout = 15, + ConnectRetryCount = 3 }; using SqlConnection sqlConnection = new(connectionStringBuilder.ConnectionString); Stopwatch timer = new(); @@ -397,15 +398,6 @@ private static bool CanCreateAliases() return false; } - using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) - { - WindowsPrincipal principal = new(identity); - if (!principal.IsInRole(WindowsBuiltInRole.Administrator)) - { - return false; - } - } - using RegistryKey key = Registry.LocalMachine.OpenSubKey(ConnectToPath, true); if (key == null) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs index 8119bd2586..24786999e7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs @@ -18,7 +18,7 @@ public static class DataClassificationTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.IsSupportedDataClassification))] public static void TestDataClassificationResultSetRank() { - s_tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); + s_tableName = DataTestUtility.GetLongName("DC"); using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) using (SqlCommand sqlCommand = sqlConnection.CreateCommand()) { @@ -41,7 +41,7 @@ public static void TestDataClassificationResultSetRank() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSupportedDataClassification))] public static void TestDataClassificationResultSet() { - s_tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); + s_tableName = DataTestUtility.GetLongName("DC"); using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) using (SqlCommand sqlCommand = sqlConnection.CreateCommand()) { @@ -232,7 +232,7 @@ public static void TestDataClassificationBulkCopy() data.Rows.Add(Guid.NewGuid(), "Company 2", "sample2@contoso.com", 1); data.Rows.Add(Guid.NewGuid(), "Company 3", "sample3@contoso.com", 1); - var tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); + var tableName = DataTestUtility.GetLongName("DC"); using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) { 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 b8731ea672..0aaaaf890d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -123,9 +123,7 @@ public static void CheckSparseColumnBit() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse))] public static void CollatedDataReaderTest() { - var databaseName = DataTestUtility.GetUniqueName("DB"); - // Remove square brackets - var dbName = databaseName.Substring(1, databaseName.Length - 2); + string dbName = DataTestUtility.GetShortName("CollationTest", false); SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString) { @@ -140,7 +138,7 @@ public static void CollatedDataReaderTest() con.Open(); // Create collated database - cmd.CommandText = $"CREATE DATABASE {databaseName} COLLATE KAZAKH_90_CI_AI"; + cmd.CommandText = $"CREATE DATABASE {dbName} COLLATE KAZAKH_90_CI_AI"; cmd.ExecuteNonQuery(); //Create connection without pooling in order to delete database later. @@ -165,7 +163,7 @@ public static void CollatedDataReaderTest() } finally { - cmd.CommandText = $"DROP DATABASE {databaseName}"; + cmd.CommandText = $"DROP DATABASE {dbName}"; cmd.ExecuteNonQuery(); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs index 30a1505442..e622121cbb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs @@ -50,7 +50,7 @@ public static async Task AsyncMultiPacketStreamRead() byte[] inputData = null; byte[] outputData = null; - string tableName = DataTestUtility.GetUniqueNameForSqlServer("data"); + string tableName = DataTestUtility.GetLongName("data"); using (SqlConnection connection = new(connectionString)) { @@ -546,7 +546,7 @@ private static void RowBuffer(string connectionString) private static void TimestampRead(string connectionString) { - string tempTable = DataTestUtility.GetUniqueNameForSqlServer("##Temp"); + string tempTable = DataTestUtility.GetLongName("##Temp"); tempTable = tempTable.Replace('-', '_'); using (SqlConnection conn = new SqlConnection(connectionString)) @@ -1041,7 +1041,7 @@ private static void SequentialAccess(string connectionString) private static void NumericRead(string connectionString) { - string tempTable = DataTestUtility.GetUniqueNameForSqlServer("##Temp"); + string tempTable = DataTestUtility.GetLongName("##Temp"); tempTable = tempTable.Replace('-', '_'); using (SqlConnection conn = new SqlConnection(connectionString)) @@ -1872,8 +1872,8 @@ private static void StreamingBlobDataTypes(string connectionString) private static void VariantCollationsTest(string connectionString) { - string dbName = DataTestUtility.GetUniqueName("JPN"); - string tableName = DataTestUtility.GetUniqueName("T"); + string dbName = DataTestUtility.GetShortName("JPN"); + string tableName = DataTestUtility.GetShortName("T"); using (SqlConnection connection = new SqlConnection(connectionString)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs index 31c232e3d0..20768e9329 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs @@ -75,7 +75,7 @@ private static void TestSimpleParameter_Type(object paramValue, string expectedT { string tag = "TestSimpleParameter_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc1"); + string procName = DataTestUtility.GetLongName("paramProc1"); try { using SqlConnection conn = new(s_connStr); @@ -115,7 +115,7 @@ private static void TestSimpleParameter_Variant(object paramValue, string expect { string tag = "TestSimpleParameter_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc2"); + string procName = DataTestUtility.GetLongName("paramProc2"); try { using SqlConnection conn = new(s_connStr); @@ -153,7 +153,7 @@ private static void TestSqlDataRecordParameterToTVP_Type(object paramValue, stri { string tag = "TestSqlDataRecordParameterToTVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); + string tvpTypeName = DataTestUtility.GetLongName("tvpType"); try { using SqlConnection conn = new(s_connStr); @@ -200,7 +200,7 @@ private static void TestSqlDataRecordParameterToTVP_Variant(object paramValue, s { string tag = "TestSqlDataRecordParameterToTVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); + string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); try { using SqlConnection conn = new(s_connStr); @@ -245,7 +245,7 @@ private static void TestSqlDataReaderParameterToTVP_Type(object paramValue, stri { string tag = "TestSqlDataReaderParameterToTVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); + string tvpTypeName = DataTestUtility.GetLongName("tvpType"); try { using SqlConnection conn = new(s_connStr); @@ -295,7 +295,7 @@ private static void TestSqlDataReaderParameterToTVP_Variant(object paramValue, s { string tag = "TestSqlDataReaderParameterToTVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); + string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); try { using SqlConnection conn = new(s_connStr); @@ -347,10 +347,10 @@ private static void TestSqlDataReader_TVP_Type(object paramValue, string expecte { string tag = "TestSqlDataReader_TVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); - string InputTableName = DataTestUtility.GetUniqueNameForSqlServer("InputTable"); - string OutputTableName = DataTestUtility.GetUniqueNameForSqlServer("OutputTable"); - string ProcName = DataTestUtility.GetUniqueNameForSqlServer("spTVPProc"); + string tvpTypeName = DataTestUtility.GetLongName("tvpType"); + string InputTableName = DataTestUtility.GetLongName("InputTable"); + string OutputTableName = DataTestUtility.GetLongName("OutputTable"); + string ProcName = DataTestUtility.GetLongName("spTVPProc"); try { using SqlConnection conn = new(s_connStr); @@ -428,10 +428,10 @@ private static void TestSqlDataReader_TVP_Variant(object paramValue, string expe { string tag = "TestSqlDataReader_TVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant_DRdrTVPVar"); - string InputTableName = DataTestUtility.GetUniqueNameForSqlServer("InputTable"); - string OutputTableName = DataTestUtility.GetUniqueNameForSqlServer("OutputTable"); - string ProcName = DataTestUtility.GetUniqueNameForSqlServer("spTVPProc_DRdrTVPVar"); + string tvpTypeName = DataTestUtility.GetLongName("tvpVariant_DRdrTVPVar"); + string InputTableName = DataTestUtility.GetLongName("InputTable"); + string OutputTableName = DataTestUtility.GetLongName("OutputTable"); + string ProcName = DataTestUtility.GetLongName("spTVPProc_DRdrTVPVar"); try { using SqlConnection conn = new(s_connStr); @@ -512,8 +512,8 @@ private static void TestSimpleDataReader_Type(object paramValue, string expected { string tag = "TestSimpleDataReader_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string inputTable = DataTestUtility.GetUniqueNameForSqlServer("inputTable"); - string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc3"); + string inputTable = DataTestUtility.GetLongName("inputTable"); + string procName = DataTestUtility.GetLongName("paramProc3"); try { using SqlConnection conn = new(s_connStr); @@ -568,8 +568,8 @@ private static void TestSimpleDataReader_Variant(object paramValue, string expec { string tag = "TestSimpleDataReader_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string inputTable = DataTestUtility.GetUniqueNameForSqlServer("inputTable"); - string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc4"); + string inputTable = DataTestUtility.GetLongName("inputTable"); + string procName = DataTestUtility.GetLongName("paramProc4"); try { using SqlConnection conn = new(s_connStr); @@ -624,8 +624,8 @@ private static void SqlBulkCopySqlDataReader_Type(object paramValue, string expe { string tag = "SqlBulkCopySqlDataReader_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopySrcTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkSrcTable"); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestTable"); + string bulkCopySrcTableName = DataTestUtility.GetLongName("bulkSrcTable"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestTable"); try { using SqlConnection conn = new(s_connStr); @@ -698,8 +698,8 @@ private static void SqlBulkCopySqlDataReader_Variant(object paramValue, string e { string tag = "SqlBulkCopySqlDataReader_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopySrcTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkSrcTable"); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestTable"); + string bulkCopySrcTableName = DataTestUtility.GetLongName("bulkSrcTable"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestTable"); try { using SqlConnection conn = new(s_connStr); @@ -776,7 +776,7 @@ private static void SqlBulkCopyDataTable_Type(object paramValue, string expected { string tag = "SqlBulkCopyDataTable_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestType"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestType"); try { using SqlConnection conn = new(s_connStr); @@ -836,7 +836,7 @@ private static void SqlBulkCopyDataTable_Variant(object paramValue, string expec { string tag = "SqlBulkCopyDataTable_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestVariant"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestVariant"); try { using SqlConnection conn = new(s_connStr); @@ -886,7 +886,7 @@ private static void SqlBulkCopyDataRow_Type(object paramValue, string expectedTy { string tag = "SqlBulkCopyDataRow_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestType"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestType"); try { using SqlConnection conn = new(s_connStr); @@ -941,7 +941,7 @@ private static void SqlBulkCopyDataRow_Variant(object paramValue, string expecte { string tag = "SqlBulkCopyDataRow_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestVariant"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestVariant"); try { using SqlConnection conn = new(s_connStr); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index 93f31bf9f1..7182ce768c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -8,6 +8,7 @@ using System.Data; using System.Data.SqlTypes; using System.Threading; +using Microsoft.Data.SqlClient.Server; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -111,7 +112,7 @@ public static void CodeCoverageSqlClient() public static void Test_Copy_SqlParameter() { using var conn = new SqlConnection(s_connString); - string cTableName = DataTestUtility.GetUniqueNameForSqlServer("#tmp"); + string cTableName = DataTestUtility.GetLongName("#tmp"); try { // Create tmp table @@ -253,9 +254,9 @@ public static void TestParametersWithDatatablesTVPInsert() }; using SqlConnection connection = new(builder.ConnectionString); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Table"); - string procName = DataTestUtility.GetUniqueNameForSqlServer("Proc"); - string typeName = DataTestUtility.GetUniqueName("Type"); + string tableName = DataTestUtility.GetLongName("Table"); + string procName = DataTestUtility.GetLongName("Proc"); + string typeName = DataTestUtility.GetShortName("Type"); try { connection.Open(); @@ -306,6 +307,126 @@ public static void TestParametersWithDatatablesTVPInsert() } } +#if !NETFRAMEWORK + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + public static void TestDateOnlyTVPDataTable_CommandSP() + { + string tableTypeName = "[dbo]." + DataTestUtility.GetLongName("UDTTTestDateOnlyTVP"); + string spName = DataTestUtility.GetLongName("spTestDateOnlyTVP"); + SqlConnection connection = new(s_connString); + try + { + connection.Open(); + using (SqlCommand cmd = connection.CreateCommand()) + { + cmd.CommandType = CommandType.Text; + cmd.CommandText = $"CREATE TYPE {tableTypeName} AS TABLE ([DateColumn] date NULL, [TimeColumn] time NULL)"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $"CREATE PROCEDURE {spName} (@dates {tableTypeName} READONLY) AS SELECT COUNT(*) FROM @dates"; + cmd.ExecuteNonQuery(); + } + using (SqlCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = spName; + cmd.CommandType = CommandType.StoredProcedure; + + DataTable dtTest = new(); + dtTest.Columns.Add(new DataColumn("DateColumn", typeof(DateOnly))); + dtTest.Columns.Add(new DataColumn("TimeColumn", typeof(TimeOnly))); + var dataRow = dtTest.NewRow(); + dataRow["DateColumn"] = new DateOnly(2023, 11, 15); + dataRow["TimeColumn"] = new TimeOnly(12, 30, 45); + dtTest.Rows.Add(dataRow); + + cmd.Parameters.Add(new SqlParameter + { + ParameterName = "@dates", + SqlDbType = SqlDbType.Structured, + TypeName = tableTypeName, + Value = dtTest, + }); + + cmd.ExecuteNonQuery(); + } + } + finally + { + DataTestUtility.DropStoredProcedure(connection, spName); + DataTestUtility.DropUserDefinedType(connection, tableTypeName); + } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + public static void TestDateOnlyTVPSqlDataRecord_CommandSP() + { + string tableTypeName = "[dbo]." + DataTestUtility.GetLongName("UDTTTestDateOnlySqlDataRecordTVP"); + string spName = DataTestUtility.GetLongName("spTestDateOnlySqlDataRecordTVP"); + SqlConnection connection = new(s_connString); + try + { + connection.Open(); + using (SqlCommand cmd = connection.CreateCommand()) + { + cmd.CommandType = CommandType.Text; + cmd.CommandText = $"CREATE TYPE {tableTypeName} AS TABLE ([DateColumn] date NULL, [TimeColumn] time NULL)"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $"CREATE PROCEDURE {spName} (@dates {tableTypeName} READONLY) AS SELECT COUNT(*) FROM @dates"; + cmd.ExecuteNonQuery(); + } + using (SqlCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = spName; + cmd.CommandType = CommandType.StoredProcedure; + + SqlMetaData[] metadata = new SqlMetaData[] + { + new SqlMetaData("DateColumn", SqlDbType.Date), + new SqlMetaData("TimeColumn", SqlDbType.Time) + }; + + SqlDataRecord record1 = new SqlDataRecord(metadata); + record1.SetValues(new DateOnly(2023, 11, 15), new TimeOnly(12, 30, 45)); + + SqlDataRecord record2 = new SqlDataRecord(metadata); + record2.SetValues(new DateOnly(2025, 11, 15), new TimeOnly(13, 31, 46)); + + IList featureInserts = new List + { + record1, + record2, + }; + + cmd.Parameters.Add(new SqlParameter + { + ParameterName = "@dates", + SqlDbType = SqlDbType.Structured, + TypeName = tableTypeName, + Value = featureInserts, + }); + + using var reader = cmd.ExecuteReader(); + + Assert.True(reader.HasRows); + + int count = 0; + while (reader.Read()) + { + Assert.NotNull(reader[0]); + count++; + } + + Assert.Equal(1, count); + } + } + finally + { + DataTestUtility.DropStoredProcedure(connection, spName); + DataTestUtility.DropUserDefinedType(connection, tableTypeName); + } + } +#endif + #region Scaled Decimal Parameter & TVP Test [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [InlineData("CAST(1.0 as decimal(38, 37))", "1.0000000000000000000000000000")] @@ -360,7 +481,7 @@ public static void SqlDecimalConvertToDecimal_TestOutOfRange(string sqlDecimalVa [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalParameter_CommandInsert(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterCMD"); + string tableName = DataTestUtility.GetLongName("TestDecimalParameterCMD"); using SqlConnection connection = InitialDatabaseTable(connectionString, tableName); try { @@ -392,7 +513,7 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalParameter_BulkCopy(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterBC"); + string tableName = DataTestUtility.GetLongName("TestDecimalParameterBC"); using SqlConnection connection = InitialDatabaseTable(connectionString, tableName); try { @@ -426,9 +547,9 @@ public static void TestScaledDecimalParameter_BulkCopy(string connectionString, [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalTVP_CommandSP(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterBC"); - string tableTypeName = DataTestUtility.GetUniqueNameForSqlServer("UDTTTestDecimalParameterBC"); - string spName = DataTestUtility.GetUniqueNameForSqlServer("spTestDecimalParameterBC"); + string tableName = DataTestUtility.GetLongName("TestDecimalParameterBC"); + string tableTypeName = DataTestUtility.GetLongName("UDTTTestDecimalParameterBC"); + string spName = DataTestUtility.GetLongName("spTestDecimalParameterBC"); using SqlConnection connection = InitialDatabaseUDTT(connectionString, tableName, tableTypeName, spName); try { @@ -713,7 +834,7 @@ private static void EnableOptimizedParameterBinding_ReturnSucceeds() { int firstInput = 12; - string sprocName = DataTestUtility.GetUniqueName("P"); + string sprocName = DataTestUtility.GetShortName("P"); // input, output string createSprocQuery = "CREATE PROCEDURE " + sprocName + " @in int " + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs index 7f383e8201..aa59bc319c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs @@ -15,7 +15,7 @@ public class SqlAdapterUpdateBatch [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void SqlAdapterTest() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Adapter"); + string tableName = DataTestUtility.GetLongName("Adapter"); string tableNameNoBrackets = tableName.Substring(1, tableName.Length - 2); try { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs index 2d11274191..e1592825b1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs @@ -108,7 +108,7 @@ private static void SendVariantParam(object paramValue, string expectedTypeName, /// private static void SendVariantBulkCopy(object paramValue, string expectedTypeName, string expectedBaseTypeName) { - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDest"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDest"); // Fetch reader using type. using SqlDataReader dr = GetReaderForVariant(paramValue, false); @@ -194,7 +194,7 @@ private static void SendVariantBulkCopy(object paramValue, string expectedTypeNa /// private static void SendVariantTvp(object paramValue, string expectedTypeName, string expectedBaseTypeName) { - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); + string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); using SqlConnection connTvp = new(s_connStr); connTvp.Open(); 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 9e3ed81af4..dc467e3775 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs @@ -240,7 +240,7 @@ public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLo public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string database = DataTestUtility.GetUniqueNameForSqlServer($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); + string database = DataTestUtility.GetLongName($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); var builder = new SqlConnectionStringBuilder(cnnString) { InitialCatalog = database, @@ -302,7 +302,7 @@ public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBase public void UpdateALockedTable(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Region"); + string tableName = DataTestUtility.GetLongName("Region"); string fieldName = "RegionDescription"; using (var cnn1 = new SqlConnection(cnnString)) 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 c78c060677..d6f1e39f77 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs @@ -58,7 +58,7 @@ public void ConnectionCancelRetryOpenInvalidCatalog(string cnnString, SqlRetryLo public void CreateDatabaseWhileTryingToConnect(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string database = DataTestUtility.GetUniqueNameForSqlServer($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); + string database = DataTestUtility.GetLongName($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); var builder = new SqlConnectionStringBuilder(cnnString) { InitialCatalog = database, diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs index 72bab47869..a845710d50 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs @@ -41,7 +41,7 @@ public static void RunTest() private static SqlDecimal BulkCopySqlDecimalToTable(SqlDecimal decimalValue, int sourcePrecision, int sourceScale, int targetPrecision, int targetScale) { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Table"); + string tableName = DataTestUtility.GetLongName("Table"); string connectionString = DataTestUtility.TCPConnectionString; SqlDecimal resultValue; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs index 823bc50a9d..2a853d7ed4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests public class AzureDistributedTransaction { private static readonly string s_connectionString = DataTestUtility.TCPConnectionString; - private static readonly string s_tableName = DataTestUtility.GetUniqueNameForSqlServer("Azure"); + private static readonly string s_tableName = DataTestUtility.GetLongName("Azure"); private static readonly string s_createTableCmd = $"CREATE TABLE {s_tableName} (NAME NVARCHAR(40), AGE INT)"; private static readonly string s_sqlBulkCopyCmd = "SELECT * FROM(VALUES ('Fuller', 33), ('Davon', 49)) AS q (FirstName, Age)"; private static readonly int s_commandTimeout = 30; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs index 5ccda71fb9..f961521233 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs @@ -12,8 +12,8 @@ public class CopyWidenNullInexactNumerics { public static void Test(string sourceDatabaseConnectionString, string destinationDatabaseConnectionString) { - string sourceTableName = DataTestUtility.GetUniqueNameForSqlServer("BCP_SRC"); - string destTableName = DataTestUtility.GetUniqueNameForSqlServer("BCP_DST"); + string sourceTableName = DataTestUtility.GetLongName("BCP_SRC"); + string destTableName = DataTestUtility.GetLongName("BCP_DST"); // this test copies float and real inexact numeric types into decimal targets using bulk copy to check that the widening of the type succeeds. using (var sourceConnection = new SqlConnection(sourceDatabaseConnectionString)) 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 4c3d594ad1..4a722dd409 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs @@ -28,7 +28,7 @@ public InitialDatabase() srcConstr = DataTestUtility.TCPConnectionString; Connection = new SqlConnection(srcConstr); - TableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBulkCopyTest_CopyStringToIntTest_"); + TableName = DataTestUtility.GetLongName("SqlBulkCopyTest_CopyStringToIntTest_"); InitialTable(Connection, TableName); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs index 8e38bee7c0..21ff771ac0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs @@ -11,7 +11,7 @@ public static class SqlCommandCompletedTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void VerifyStatmentCompletedCalled() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("stmt"); + string tableName = DataTestUtility.GetLongName("stmt"); using (var conn = new SqlConnection(s_connStr)) using (var cmd = conn.CreateCommand()) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs index 26b11055c2..7f28a4a09a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs @@ -15,8 +15,8 @@ public class SqlCommandSetTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void TestByteArrayParameters() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("CMD"); - string procName = DataTestUtility.GetUniqueNameForSqlServer("CMD"); + string tableName = DataTestUtility.GetLongName("CMD"); + string procName = DataTestUtility.GetLongName("CMD"); byte[] bArray = new byte[] { 1, 2, 3 }; using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs index 09c369e11e..3bea30c6b8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs @@ -221,7 +221,7 @@ private static string SetupFileStreamDB() fileStreamDir += "\\"; } - string dbName = DataTestUtility.GetUniqueName("FS", false); + string dbName = DataTestUtility.GetShortName("FS", false); string createDBQuery = @$"CREATE DATABASE [{dbName}] ON PRIMARY (NAME = PhotoLibrary_data, @@ -266,7 +266,7 @@ private static void DropFileStreamDb(string connString) private static string SetupTable(string connString) { // Generate random table name - string tempTable = DataTestUtility.GetUniqueNameForSqlServer("fs"); + string tempTable = DataTestUtility.GetLongName("fs"); // Create table string createTable = $"CREATE TABLE {tempTable} (EmployeeId INT NOT NULL PRIMARY KEY, Photo VARBINARY(MAX) FILESTREAM NULL, RowGuid UNIQUEIDENTIFIER NOT NULL ROWGUIDCOL UNIQUE DEFAULT NEWID() ) "; ExecuteNonQueryCommand(createTable, connString); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs index 0e64b091b6..8f697af02e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs @@ -434,7 +434,7 @@ private static string GetUdtName(Type udtClrType) [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void TestSqlServerTypesInsertAndRead() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Type"); + string tableName = DataTestUtility.GetLongName("Type"); string allTypesSQL = @$" if not exists (select * from sysobjects where name='{tableName}' and xtype='U') Begin diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs index 90ceed4952..5f17036b4f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs @@ -18,9 +18,9 @@ public void RunCopyTest() _connStr = (new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { InitialCatalog = DataTestUtility.UdtTestDbName }).ConnectionString; SqlConnection conn = new SqlConnection(_connStr); - string cities = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_cities"); - string customers = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_customers"); - string circles = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_circles"); + string cities = DataTestUtility.GetLongName("UdtBulkCopy_cities"); + string customers = DataTestUtility.GetLongName("UdtBulkCopy_customers"); + string circles = DataTestUtility.GetLongName("UdtBulkCopy_circles"); conn.Open(); try 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 1f07242ee3..ef38e43dda 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -84,8 +84,8 @@ public void UDTParams_Binary() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_Invalid2() { - string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2"); + string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetLongName("UdtTest2"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -143,8 +143,8 @@ public void UDTParams_Invalid() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_TypedNull() { - string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2_Customer"); + string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetLongName("UdtTest2_Customer"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -188,8 +188,8 @@ public void UDTParams_TypedNull() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_NullInput() { - string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2_Customer"); + string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetLongName("UdtTest2_Customer"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -232,8 +232,8 @@ public void UDTParams_NullInput() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_InputOutput() { - string spInsertCity = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCity"); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2"); + string spInsertCity = DataTestUtility.GetLongName("spUdtTest2_InsertCity"); + string tableName = DataTestUtility.GetLongName("UdtTest2"); using (SqlConnection conn = new SqlConnection(_connStr)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs index effecb35b3..41f81b12e3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs @@ -37,7 +37,7 @@ public static void CheckSupportUtf8ConnectionProperty() public static void UTF8databaseTest() { const string letters = @"!\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f€\u0081‚ƒ„…†‡ˆ‰Š‹Œ\u008dŽ\u008f\u0090‘’“”•–—˜™š›œ\u009džŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"; - string dbName = DataTestUtility.GetUniqueNameForSqlServer("UTF8databaseTest", false); + string dbName = DataTestUtility.GetLongName("UTF8databaseTest", false); string tblName = "Table1"; SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); From ee2d196f1dc12c80f4b2afb70b5d895bd7fac4b8 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Mon, 8 Sep 2025 09:40:10 -0300 Subject: [PATCH 63/76] Revert "User Story 38481: Fix unique db object name issues" This reverts commit 82ce7cf7d4b4a7b75f46ac55b0ee842c6b333f48. --- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 10 +- .../AlwaysEncrypted/CspProviderExt.cs | 49 +++++ .../ManualTests/DataCommon/DataTestUtility.cs | 202 ++++-------------- .../ProviderAgnostic/ReaderTest/ReaderTest.cs | 6 +- .../SQL/AdapterTest/AdapterTest.cs | 10 +- .../SQL/ConnectivityTests/ConnectivityTest.cs | 14 +- .../DataClassificationTest.cs | 6 +- .../SQL/DataReaderTest/DataReaderTest.cs | 8 +- .../SQL/DataStreamTest/DataStreamTest.cs | 10 +- .../SQL/ParameterTest/DateTimeVariantTest.cs | 52 ++--- .../SQL/ParameterTest/ParametersTest.cs | 141 +----------- .../ParameterTest/SqlAdapterUpdateBatch.cs | 2 +- .../SQL/ParameterTest/SqlVariantParam.cs | 4 +- .../RetryLogic/SqlCommandReliabilityTest.cs | 4 +- .../SqlConnectionReliabilityTest.cs | 2 +- .../AdjustPrecScaleForBulkCopy.cs | 2 +- .../AzureDistributedTransaction.cs | 2 +- .../CopyWidenNullInexactNumerics.cs | 4 +- .../DataConversionErrorMessageTest.cs | 2 +- .../SQL/SqlCommand/SqlCommandCompletedTest.cs | 2 +- .../SQL/SqlCommand/SqlCommandSetTest.cs | 4 +- .../SqlFileStreamTest/SqlFileStreamTest.cs | 4 +- .../SQL/UdtTest/SqlServerTypesTest.cs | 2 +- .../SQL/UdtTest/UdtBulkCopyTest.cs | 6 +- .../tests/ManualTests/SQL/UdtTest/UdtTest2.cs | 16 +- .../SQL/Utf8SupportTest/Utf8SupportTest.cs | 2 +- 26 files changed, 188 insertions(+), 378 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index b987bdbc99..8e7458f02e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -148,8 +148,8 @@ public void SqlParameterProperties(string connection) const string firstColumnName = @"firstColumn"; const string secondColumnName = @"secondColumn"; const string thirdColumnName = @"thirdColumn"; - string inputProcedureName = DataTestUtility.GetShortName("InputProc").ToString(); - string outputProcedureName = DataTestUtility.GetShortName("OutputProc").ToString(); + string inputProcedureName = DataTestUtility.GetUniqueName("InputProc").ToString(); + string outputProcedureName = DataTestUtility.GetUniqueName("OutputProc").ToString(); const int charColumnSize = 100; const int decimalColumnPrecision = 10; const int decimalColumnScale = 4; @@ -694,7 +694,7 @@ public void TestExecuteReader(string connection) [ClassData(typeof(AEConnectionStringProvider))] public async void TestExecuteReaderAsyncWithLargeQuery(string connectionString) { - string randomName = DataTestUtility.GetShortName(Guid.NewGuid().ToString().Replace("-", ""), false); + string randomName = DataTestUtility.GetUniqueName(Guid.NewGuid().ToString().Replace("-", ""), false); if (randomName.Length > 50) { randomName = randomName.Substring(0, 50); @@ -878,8 +878,8 @@ public void TestEnclaveStoredProceduresWithAndWithoutParameters(string connectio using SqlCommand sqlCommand = new("", sqlConnection, transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled); - string procWithoutParams = DataTestUtility.GetShortName("EnclaveWithoutParams", withBracket: false); - string procWithParam = DataTestUtility.GetShortName("EnclaveWithParams", withBracket: false); + string procWithoutParams = DataTestUtility.GetUniqueName("EnclaveWithoutParams", withBracket: false); + string procWithParam = DataTestUtility.GetUniqueName("EnclaveWithParams", withBracket: false); try { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs index e26c67382e..49071def2e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs @@ -157,6 +157,55 @@ public void TestRoundTripWithCSPAndCertStoreProvider() } } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestEncryptDecryptWithCSP(string connectionString) + { + string providerName = @"Microsoft Enhanced RSA and AES Cryptographic Provider"; + string keyIdentifier = DataTestUtility.GetUniqueNameForSqlServer("CSP"); + + try + { + CertificateUtilityWin.RSAPersistKeyInCsp(providerName, keyIdentifier); + string cspPath = String.Concat(providerName, @"/", keyIdentifier); + + SQLSetupStrategyCspExt sqlSetupStrategyCsp = new SQLSetupStrategyCspExt(cspPath); + string tableName = sqlSetupStrategyCsp.CspProviderTable.Name; + + try + { + using SqlConnection sqlConn = new(connectionString); + sqlConn.Open(); + + Table.DeleteData(tableName, sqlConn); + + // insert 1 row data + Customer customer = new Customer(45, "Microsoft", "Corporation"); + + DatabaseHelper.InsertCustomerData(sqlConn, null, tableName, customer); + + // Test INPUT parameter on an encrypted parameter + using SqlCommand sqlCommand = new(@$"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @firstName", + sqlConn, null, SqlCommandColumnEncryptionSetting.Enabled); + SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); + Console.WriteLine(@"Exception: {0}"); + customerFirstParam.Direction = System.Data.ParameterDirection.Input; + + using SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(); + ValidateResultSet(sqlDataReader); + } + finally + { + // clean up database resources + sqlSetupStrategyCsp.Dispose(); + } + } + finally + { + CertificateUtilityWin.RSADeleteKeyInCsp(providerName, keyIdentifier); + } + } + /// /// Validates that the results are the ones expected. /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 2723853f27..1375cbea2f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -88,7 +88,7 @@ public static bool TcpConnectionStringDoesNotUseAadAuth { get { - SqlConnectionStringBuilder builder = new(TCPConnectionString); + SqlConnectionStringBuilder builder = new (TCPConnectionString); return builder.Authentication == SqlAuthenticationMethod.SqlPassword || builder.Authentication == SqlAuthenticationMethod.NotSpecified; } } @@ -415,176 +415,48 @@ public static bool DoesHostAddressContainBothIPv4AndIPv6() } } - // Generate a new GUID and return the characters from its 1st and 4th - // parts, as shown here: - // - // 7ff01cb8-88c7-11f0-b433-00155d7e531e - // ^^^^^^^^ ^^^^ - // - // These 12 characters are concatenated together without any - // separators. These 2 parts typically comprise a timestamp and clock - // sequence, most likely to be unique for tests that generate names in - // quick succession. - private static string GetGuidParts() - { - var guid = Guid.NewGuid().ToString(); - // GOTCHA: The slice operator is inclusive of the start index and - // exclusive of the end index! - return guid.Substring(0, 8) + guid.Substring(19, 4); - } - /// - /// Generate a short unique database object name, whose maximum length - /// is 30 characters, with the format: - /// - /// _ - /// - /// The Prefix will be truncated to satisfy the overall maximum length. - /// - /// The GUID parts will be the characters from the 1st and 4th blocks - /// from a traditional string representation, as shown here: - /// - /// 7ff01cb8-88c7-11f0-b433-00155d7e531e - /// ^^^^^^^^ ^^^^ - /// - /// These 2 parts typically comprise a timestamp and clock sequence, - /// most likely to be unique for tests that generate names in quick - /// succession. The 12 characters are concatenated together without any - /// separators. + /// Generate a unique name to use in Sql Server; + /// some providers does not support names (Oracle supports up to 30). /// - /// - /// - /// The prefix to use when generating the unique name, truncated to at - /// most 18 characters when withBracket is false, and 16 characters when - /// withBracket is true. - /// - /// This should not contain any characters that cannot be used in - /// database object names. See: - /// - /// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers - /// - /// - /// - /// When true, the entire generated name will be enclosed in square - /// brackets, for example: - /// - /// [MyPrefix_7ff01cb811f0] - /// - /// - /// - /// A unique database object name, no more than 30 characters long. - /// - public static string GetShortName(string prefix, bool withBracket = true) - { - StringBuilder name = new(30); - - if (withBracket) - { - name.Append('['); - } - - int maxPrefixLength = withBracket ? 16 : 18; - if (prefix.Length > maxPrefixLength) - { - prefix = prefix.Substring(0, maxPrefixLength); - } - - name.Append(prefix); - name.Append('_'); - name.Append(GetGuidParts()); - - if (withBracket) - { - name.Append(']'); - } - - return name.ToString(); + /// The name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length). + /// Name without brackets. + /// Unique name by considering the Sql Server naming rules. + public static string GetUniqueName(string prefix, bool withBracket = true) + { + string escapeLeft = withBracket ? "[" : string.Empty; + string escapeRight = withBracket ? "]" : string.Empty; + string uniqueName = string.Format("{0}{1}_{2}_{3}{4}", + escapeLeft, + prefix, + DateTime.Now.Ticks.ToString("X", CultureInfo.InvariantCulture), // up to 8 characters + Guid.NewGuid().ToString().Substring(0, 6), // take the first 6 characters only + escapeRight); + return uniqueName; } /// - /// Generate a long unique database object name, whose maximum length is - /// 96 characters, with the format: - /// - /// ___ - /// - /// The Prefix will be truncated to satisfy the overall maximum length. - /// - /// The GUID Parts will be the characters from the 1st and 4th blocks - /// from a traditional string representation, as shown here: - /// - /// 7ff01cb8-88c7-11f0-b433-00155d7e531e - /// ^^^^^^^^ ^^^^ - /// - /// These 2 parts typically comprise a timestamp and clock sequence, - /// most likely to be unique for tests that generate names in quick - /// succession. The 12 characters are concatenated together without any - /// separators. - /// - /// The UserName and MachineName are obtained from the Environment, - /// and will be truncated to satisfy the maximum overall length. + /// Uses environment values `UserName` and `MachineName` in addition to the specified `prefix` and current date + /// to generate a unique name to use in Sql Server; + /// SQL Server supports long names (up to 128 characters), add extra info for troubleshooting. /// - /// - /// - /// The prefix to use when generating the unique name, truncated to at - /// most 32 characters. - /// - /// This should not contain any characters that cannot be used in - /// database object names. See: - /// - /// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers - /// - /// - /// - /// When true, the entire generated name will be enclosed in square - /// brackets, for example: - /// - /// [MyPrefix_7ff01cb811f0_test_user_ci_agent_machine_name] - /// - /// - /// - /// A unique database object name, no more than 96 characters long. - /// - public static string GetLongName(string prefix, bool withBracket = true) - { - StringBuilder name = new(96); - - if (withBracket) - { - name.Append('['); - } - - if (prefix.Length > 32) - { - prefix = prefix.Substring(0, 32); - } - - name.Append(prefix); - name.Append('_'); - name.Append(GetGuidParts()); - name.Append('_'); - - var suffix = - Environment.UserName + '_' + - Environment.MachineName; - - int maxSuffixLength = 96 - name.Length; - if (withBracket) - { - --maxSuffixLength; - } - if (suffix.Length > maxSuffixLength) - { - suffix = suffix.Substring(0, maxSuffixLength); - } - - name.Append(suffix); - - if (withBracket) - { - name.Append(']'); - } - - return name.ToString(); + /// Add the prefix to the generate string. + /// Database name must be pass with brackets by default. + /// Unique name by considering the Sql Server naming rules. + public static string GetUniqueNameForSqlServer(string prefix, bool withBracket = true) + { + string extendedPrefix = string.Format( + "{0}_{1}_{2}@{3}", + prefix, + Environment.UserName, + Environment.MachineName, + DateTime.Now.ToString("yyyy_MM_dd", CultureInfo.InvariantCulture)); + string name = GetUniqueName(extendedPrefix, withBracket); + if (name.Length > 128) + { + throw new ArgumentOutOfRangeException("the name is too long - SQL Server names are limited to 128"); + } + return name; } public static bool IsSupportingDistributedTransactions() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs index 5728a8b1ac..5d09be77f4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs @@ -19,7 +19,7 @@ public static void TestMain() { string connectionString = DataTestUtility.TCPConnectionString; - string tempTable = DataTestUtility.GetLongName("table"); + string tempTable = DataTestUtility.GetUniqueNameForSqlServer("table"); DbProviderFactory provider = SqlClientFactory.Instance; try @@ -275,7 +275,7 @@ public static void TestMain() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void SqlDataReader_SqlBuffer_GetFieldValue() { - string tableName = DataTestUtility.GetLongName("SqlBuffer_GetFieldValue"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBuffer_GetFieldValue"); DateTimeOffset dtoffset = DateTimeOffset.Now; DateTime dt = DateTime.Now; //Exclude the millisecond because of rounding at some points by SQL Server. @@ -374,7 +374,7 @@ public static void SqlDataReader_SqlBuffer_GetFieldValue() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async Task SqlDataReader_SqlBuffer_GetFieldValue_Async() { - string tableName = DataTestUtility.GetLongName("SqlBuffer_GetFieldValue_Async"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBuffer_GetFieldValue_Async"); DateTimeOffset dtoffset = DateTimeOffset.Now; DateTime dt = DateTime.Now; //Exclude the millisecond because of rounding at some points by SQL Server. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs index 371550e105..9d43567cf1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs @@ -54,7 +54,7 @@ public class AdapterTest public AdapterTest() { // create random name for temp tables - _tempTable = DataTestUtility.GetShortName("AdapterTest"); + _tempTable = DataTestUtility.GetUniqueName("AdapterTest"); _tempTable = _tempTable.Replace('-', '_'); _randomGuid = Guid.NewGuid().ToString(); @@ -487,7 +487,7 @@ public void ParameterTest_AllTypes() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void ParameterTest_InOut() { - string procName = DataTestUtility.GetShortName("P"); + string procName = DataTestUtility.GetUniqueName("P"); // input, output string spCreateInOut = "CREATE PROCEDURE " + procName + " @in int, @inout int OUTPUT, @out nvarchar(8) OUTPUT " + @@ -768,13 +768,13 @@ public void BulkUpdateTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void UpdateRefreshTest() { - string identTableName = DataTestUtility.GetShortName("ID_"); + string identTableName = DataTestUtility.GetUniqueName("ID_"); string createIdentTable = $"CREATE TABLE {identTableName} (id int IDENTITY," + "LastName nvarchar(50) NULL," + "Firstname nvarchar(50) NULL)"; - string spName = DataTestUtility.GetShortName("sp_insert", withBracket: false); + string spName = DataTestUtility.GetUniqueName("sp_insert", withBracket: false); string spCreateInsert = $"CREATE PROCEDURE {spName}" + "(@FirstName nvarchar(50), @LastName nvarchar(50), @id int OUTPUT) " + @@ -1087,7 +1087,7 @@ public void AutoGenUpdateTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void AutoGenErrorTest() { - string identTableName = DataTestUtility.GetShortName("ID_"); + string identTableName = DataTestUtility.GetUniqueName("ID_"); string createIdentTable = $"CREATE TABLE {identTableName} (id int IDENTITY," + "LastName nvarchar(50) 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 dfdbf5229f..7675643382 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -368,10 +368,9 @@ public static void ConnectionOpenDisableRetry() { SqlConnectionStringBuilder connectionStringBuilder = new(DataTestUtility.TCPConnectionString) { - InitialCatalog = DataTestUtility.GetLongName("DoesNotExist", false), + InitialCatalog = "DoesNotExist0982532435423", Pooling = false, - ConnectTimeout = 15, - ConnectRetryCount = 3 + ConnectTimeout=15 }; using SqlConnection sqlConnection = new(connectionStringBuilder.ConnectionString); Stopwatch timer = new(); @@ -398,6 +397,15 @@ private static bool CanCreateAliases() return false; } + using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) + { + WindowsPrincipal principal = new(identity); + if (!principal.IsInRole(WindowsBuiltInRole.Administrator)) + { + return false; + } + } + using RegistryKey key = Registry.LocalMachine.OpenSubKey(ConnectToPath, true); if (key == null) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs index 24786999e7..8119bd2586 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs @@ -18,7 +18,7 @@ public static class DataClassificationTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.IsSupportedDataClassification))] public static void TestDataClassificationResultSetRank() { - s_tableName = DataTestUtility.GetLongName("DC"); + s_tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) using (SqlCommand sqlCommand = sqlConnection.CreateCommand()) { @@ -41,7 +41,7 @@ public static void TestDataClassificationResultSetRank() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSupportedDataClassification))] public static void TestDataClassificationResultSet() { - s_tableName = DataTestUtility.GetLongName("DC"); + s_tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) using (SqlCommand sqlCommand = sqlConnection.CreateCommand()) { @@ -232,7 +232,7 @@ public static void TestDataClassificationBulkCopy() data.Rows.Add(Guid.NewGuid(), "Company 2", "sample2@contoso.com", 1); data.Rows.Add(Guid.NewGuid(), "Company 3", "sample3@contoso.com", 1); - var tableName = DataTestUtility.GetLongName("DC"); + var tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) { 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 0aaaaf890d..b8731ea672 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -123,7 +123,9 @@ public static void CheckSparseColumnBit() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse))] public static void CollatedDataReaderTest() { - string dbName = DataTestUtility.GetShortName("CollationTest", false); + var databaseName = DataTestUtility.GetUniqueName("DB"); + // Remove square brackets + var dbName = databaseName.Substring(1, databaseName.Length - 2); SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString) { @@ -138,7 +140,7 @@ public static void CollatedDataReaderTest() con.Open(); // Create collated database - cmd.CommandText = $"CREATE DATABASE {dbName} COLLATE KAZAKH_90_CI_AI"; + cmd.CommandText = $"CREATE DATABASE {databaseName} COLLATE KAZAKH_90_CI_AI"; cmd.ExecuteNonQuery(); //Create connection without pooling in order to delete database later. @@ -163,7 +165,7 @@ public static void CollatedDataReaderTest() } finally { - cmd.CommandText = $"DROP DATABASE {dbName}"; + cmd.CommandText = $"DROP DATABASE {databaseName}"; cmd.ExecuteNonQuery(); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs index e622121cbb..30a1505442 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs @@ -50,7 +50,7 @@ public static async Task AsyncMultiPacketStreamRead() byte[] inputData = null; byte[] outputData = null; - string tableName = DataTestUtility.GetLongName("data"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("data"); using (SqlConnection connection = new(connectionString)) { @@ -546,7 +546,7 @@ private static void RowBuffer(string connectionString) private static void TimestampRead(string connectionString) { - string tempTable = DataTestUtility.GetLongName("##Temp"); + string tempTable = DataTestUtility.GetUniqueNameForSqlServer("##Temp"); tempTable = tempTable.Replace('-', '_'); using (SqlConnection conn = new SqlConnection(connectionString)) @@ -1041,7 +1041,7 @@ private static void SequentialAccess(string connectionString) private static void NumericRead(string connectionString) { - string tempTable = DataTestUtility.GetLongName("##Temp"); + string tempTable = DataTestUtility.GetUniqueNameForSqlServer("##Temp"); tempTable = tempTable.Replace('-', '_'); using (SqlConnection conn = new SqlConnection(connectionString)) @@ -1872,8 +1872,8 @@ private static void StreamingBlobDataTypes(string connectionString) private static void VariantCollationsTest(string connectionString) { - string dbName = DataTestUtility.GetShortName("JPN"); - string tableName = DataTestUtility.GetShortName("T"); + string dbName = DataTestUtility.GetUniqueName("JPN"); + string tableName = DataTestUtility.GetUniqueName("T"); using (SqlConnection connection = new SqlConnection(connectionString)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs index 20768e9329..31c232e3d0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs @@ -75,7 +75,7 @@ private static void TestSimpleParameter_Type(object paramValue, string expectedT { string tag = "TestSimpleParameter_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string procName = DataTestUtility.GetLongName("paramProc1"); + string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc1"); try { using SqlConnection conn = new(s_connStr); @@ -115,7 +115,7 @@ private static void TestSimpleParameter_Variant(object paramValue, string expect { string tag = "TestSimpleParameter_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string procName = DataTestUtility.GetLongName("paramProc2"); + string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc2"); try { using SqlConnection conn = new(s_connStr); @@ -153,7 +153,7 @@ private static void TestSqlDataRecordParameterToTVP_Type(object paramValue, stri { string tag = "TestSqlDataRecordParameterToTVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetLongName("tvpType"); + string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); try { using SqlConnection conn = new(s_connStr); @@ -200,7 +200,7 @@ private static void TestSqlDataRecordParameterToTVP_Variant(object paramValue, s { string tag = "TestSqlDataRecordParameterToTVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); + string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); try { using SqlConnection conn = new(s_connStr); @@ -245,7 +245,7 @@ private static void TestSqlDataReaderParameterToTVP_Type(object paramValue, stri { string tag = "TestSqlDataReaderParameterToTVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetLongName("tvpType"); + string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); try { using SqlConnection conn = new(s_connStr); @@ -295,7 +295,7 @@ private static void TestSqlDataReaderParameterToTVP_Variant(object paramValue, s { string tag = "TestSqlDataReaderParameterToTVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); + string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); try { using SqlConnection conn = new(s_connStr); @@ -347,10 +347,10 @@ private static void TestSqlDataReader_TVP_Type(object paramValue, string expecte { string tag = "TestSqlDataReader_TVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetLongName("tvpType"); - string InputTableName = DataTestUtility.GetLongName("InputTable"); - string OutputTableName = DataTestUtility.GetLongName("OutputTable"); - string ProcName = DataTestUtility.GetLongName("spTVPProc"); + string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); + string InputTableName = DataTestUtility.GetUniqueNameForSqlServer("InputTable"); + string OutputTableName = DataTestUtility.GetUniqueNameForSqlServer("OutputTable"); + string ProcName = DataTestUtility.GetUniqueNameForSqlServer("spTVPProc"); try { using SqlConnection conn = new(s_connStr); @@ -428,10 +428,10 @@ private static void TestSqlDataReader_TVP_Variant(object paramValue, string expe { string tag = "TestSqlDataReader_TVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetLongName("tvpVariant_DRdrTVPVar"); - string InputTableName = DataTestUtility.GetLongName("InputTable"); - string OutputTableName = DataTestUtility.GetLongName("OutputTable"); - string ProcName = DataTestUtility.GetLongName("spTVPProc_DRdrTVPVar"); + string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant_DRdrTVPVar"); + string InputTableName = DataTestUtility.GetUniqueNameForSqlServer("InputTable"); + string OutputTableName = DataTestUtility.GetUniqueNameForSqlServer("OutputTable"); + string ProcName = DataTestUtility.GetUniqueNameForSqlServer("spTVPProc_DRdrTVPVar"); try { using SqlConnection conn = new(s_connStr); @@ -512,8 +512,8 @@ private static void TestSimpleDataReader_Type(object paramValue, string expected { string tag = "TestSimpleDataReader_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string inputTable = DataTestUtility.GetLongName("inputTable"); - string procName = DataTestUtility.GetLongName("paramProc3"); + string inputTable = DataTestUtility.GetUniqueNameForSqlServer("inputTable"); + string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc3"); try { using SqlConnection conn = new(s_connStr); @@ -568,8 +568,8 @@ private static void TestSimpleDataReader_Variant(object paramValue, string expec { string tag = "TestSimpleDataReader_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string inputTable = DataTestUtility.GetLongName("inputTable"); - string procName = DataTestUtility.GetLongName("paramProc4"); + string inputTable = DataTestUtility.GetUniqueNameForSqlServer("inputTable"); + string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc4"); try { using SqlConnection conn = new(s_connStr); @@ -624,8 +624,8 @@ private static void SqlBulkCopySqlDataReader_Type(object paramValue, string expe { string tag = "SqlBulkCopySqlDataReader_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopySrcTableName = DataTestUtility.GetLongName("bulkSrcTable"); - string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestTable"); + string bulkCopySrcTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkSrcTable"); + string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestTable"); try { using SqlConnection conn = new(s_connStr); @@ -698,8 +698,8 @@ private static void SqlBulkCopySqlDataReader_Variant(object paramValue, string e { string tag = "SqlBulkCopySqlDataReader_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopySrcTableName = DataTestUtility.GetLongName("bulkSrcTable"); - string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestTable"); + string bulkCopySrcTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkSrcTable"); + string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestTable"); try { using SqlConnection conn = new(s_connStr); @@ -776,7 +776,7 @@ private static void SqlBulkCopyDataTable_Type(object paramValue, string expected { string tag = "SqlBulkCopyDataTable_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestType"); + string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestType"); try { using SqlConnection conn = new(s_connStr); @@ -836,7 +836,7 @@ private static void SqlBulkCopyDataTable_Variant(object paramValue, string expec { string tag = "SqlBulkCopyDataTable_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestVariant"); + string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestVariant"); try { using SqlConnection conn = new(s_connStr); @@ -886,7 +886,7 @@ private static void SqlBulkCopyDataRow_Type(object paramValue, string expectedTy { string tag = "SqlBulkCopyDataRow_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestType"); + string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestType"); try { using SqlConnection conn = new(s_connStr); @@ -941,7 +941,7 @@ private static void SqlBulkCopyDataRow_Variant(object paramValue, string expecte { string tag = "SqlBulkCopyDataRow_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestVariant"); + string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestVariant"); try { using SqlConnection conn = new(s_connStr); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index 7182ce768c..93f31bf9f1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -8,7 +8,6 @@ using System.Data; using System.Data.SqlTypes; using System.Threading; -using Microsoft.Data.SqlClient.Server; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -112,7 +111,7 @@ public static void CodeCoverageSqlClient() public static void Test_Copy_SqlParameter() { using var conn = new SqlConnection(s_connString); - string cTableName = DataTestUtility.GetLongName("#tmp"); + string cTableName = DataTestUtility.GetUniqueNameForSqlServer("#tmp"); try { // Create tmp table @@ -254,9 +253,9 @@ public static void TestParametersWithDatatablesTVPInsert() }; using SqlConnection connection = new(builder.ConnectionString); - string tableName = DataTestUtility.GetLongName("Table"); - string procName = DataTestUtility.GetLongName("Proc"); - string typeName = DataTestUtility.GetShortName("Type"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("Table"); + string procName = DataTestUtility.GetUniqueNameForSqlServer("Proc"); + string typeName = DataTestUtility.GetUniqueName("Type"); try { connection.Open(); @@ -307,126 +306,6 @@ public static void TestParametersWithDatatablesTVPInsert() } } -#if !NETFRAMEWORK - - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] - public static void TestDateOnlyTVPDataTable_CommandSP() - { - string tableTypeName = "[dbo]." + DataTestUtility.GetLongName("UDTTTestDateOnlyTVP"); - string spName = DataTestUtility.GetLongName("spTestDateOnlyTVP"); - SqlConnection connection = new(s_connString); - try - { - connection.Open(); - using (SqlCommand cmd = connection.CreateCommand()) - { - cmd.CommandType = CommandType.Text; - cmd.CommandText = $"CREATE TYPE {tableTypeName} AS TABLE ([DateColumn] date NULL, [TimeColumn] time NULL)"; - cmd.ExecuteNonQuery(); - cmd.CommandText = $"CREATE PROCEDURE {spName} (@dates {tableTypeName} READONLY) AS SELECT COUNT(*) FROM @dates"; - cmd.ExecuteNonQuery(); - } - using (SqlCommand cmd = connection.CreateCommand()) - { - cmd.CommandText = spName; - cmd.CommandType = CommandType.StoredProcedure; - - DataTable dtTest = new(); - dtTest.Columns.Add(new DataColumn("DateColumn", typeof(DateOnly))); - dtTest.Columns.Add(new DataColumn("TimeColumn", typeof(TimeOnly))); - var dataRow = dtTest.NewRow(); - dataRow["DateColumn"] = new DateOnly(2023, 11, 15); - dataRow["TimeColumn"] = new TimeOnly(12, 30, 45); - dtTest.Rows.Add(dataRow); - - cmd.Parameters.Add(new SqlParameter - { - ParameterName = "@dates", - SqlDbType = SqlDbType.Structured, - TypeName = tableTypeName, - Value = dtTest, - }); - - cmd.ExecuteNonQuery(); - } - } - finally - { - DataTestUtility.DropStoredProcedure(connection, spName); - DataTestUtility.DropUserDefinedType(connection, tableTypeName); - } - } - - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] - public static void TestDateOnlyTVPSqlDataRecord_CommandSP() - { - string tableTypeName = "[dbo]." + DataTestUtility.GetLongName("UDTTTestDateOnlySqlDataRecordTVP"); - string spName = DataTestUtility.GetLongName("spTestDateOnlySqlDataRecordTVP"); - SqlConnection connection = new(s_connString); - try - { - connection.Open(); - using (SqlCommand cmd = connection.CreateCommand()) - { - cmd.CommandType = CommandType.Text; - cmd.CommandText = $"CREATE TYPE {tableTypeName} AS TABLE ([DateColumn] date NULL, [TimeColumn] time NULL)"; - cmd.ExecuteNonQuery(); - cmd.CommandText = $"CREATE PROCEDURE {spName} (@dates {tableTypeName} READONLY) AS SELECT COUNT(*) FROM @dates"; - cmd.ExecuteNonQuery(); - } - using (SqlCommand cmd = connection.CreateCommand()) - { - cmd.CommandText = spName; - cmd.CommandType = CommandType.StoredProcedure; - - SqlMetaData[] metadata = new SqlMetaData[] - { - new SqlMetaData("DateColumn", SqlDbType.Date), - new SqlMetaData("TimeColumn", SqlDbType.Time) - }; - - SqlDataRecord record1 = new SqlDataRecord(metadata); - record1.SetValues(new DateOnly(2023, 11, 15), new TimeOnly(12, 30, 45)); - - SqlDataRecord record2 = new SqlDataRecord(metadata); - record2.SetValues(new DateOnly(2025, 11, 15), new TimeOnly(13, 31, 46)); - - IList featureInserts = new List - { - record1, - record2, - }; - - cmd.Parameters.Add(new SqlParameter - { - ParameterName = "@dates", - SqlDbType = SqlDbType.Structured, - TypeName = tableTypeName, - Value = featureInserts, - }); - - using var reader = cmd.ExecuteReader(); - - Assert.True(reader.HasRows); - - int count = 0; - while (reader.Read()) - { - Assert.NotNull(reader[0]); - count++; - } - - Assert.Equal(1, count); - } - } - finally - { - DataTestUtility.DropStoredProcedure(connection, spName); - DataTestUtility.DropUserDefinedType(connection, tableTypeName); - } - } -#endif - #region Scaled Decimal Parameter & TVP Test [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [InlineData("CAST(1.0 as decimal(38, 37))", "1.0000000000000000000000000000")] @@ -481,7 +360,7 @@ public static void SqlDecimalConvertToDecimal_TestOutOfRange(string sqlDecimalVa [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalParameter_CommandInsert(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetLongName("TestDecimalParameterCMD"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterCMD"); using SqlConnection connection = InitialDatabaseTable(connectionString, tableName); try { @@ -513,7 +392,7 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalParameter_BulkCopy(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetLongName("TestDecimalParameterBC"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterBC"); using SqlConnection connection = InitialDatabaseTable(connectionString, tableName); try { @@ -547,9 +426,9 @@ public static void TestScaledDecimalParameter_BulkCopy(string connectionString, [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalTVP_CommandSP(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetLongName("TestDecimalParameterBC"); - string tableTypeName = DataTestUtility.GetLongName("UDTTTestDecimalParameterBC"); - string spName = DataTestUtility.GetLongName("spTestDecimalParameterBC"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterBC"); + string tableTypeName = DataTestUtility.GetUniqueNameForSqlServer("UDTTTestDecimalParameterBC"); + string spName = DataTestUtility.GetUniqueNameForSqlServer("spTestDecimalParameterBC"); using SqlConnection connection = InitialDatabaseUDTT(connectionString, tableName, tableTypeName, spName); try { @@ -834,7 +713,7 @@ private static void EnableOptimizedParameterBinding_ReturnSucceeds() { int firstInput = 12; - string sprocName = DataTestUtility.GetShortName("P"); + string sprocName = DataTestUtility.GetUniqueName("P"); // input, output string createSprocQuery = "CREATE PROCEDURE " + sprocName + " @in int " + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs index aa59bc319c..7f383e8201 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs @@ -15,7 +15,7 @@ public class SqlAdapterUpdateBatch [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void SqlAdapterTest() { - string tableName = DataTestUtility.GetLongName("Adapter"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("Adapter"); string tableNameNoBrackets = tableName.Substring(1, tableName.Length - 2); try { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs index e1592825b1..2d11274191 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs @@ -108,7 +108,7 @@ private static void SendVariantParam(object paramValue, string expectedTypeName, /// private static void SendVariantBulkCopy(object paramValue, string expectedTypeName, string expectedBaseTypeName) { - string bulkCopyTableName = DataTestUtility.GetLongName("bulkDest"); + string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDest"); // Fetch reader using type. using SqlDataReader dr = GetReaderForVariant(paramValue, false); @@ -194,7 +194,7 @@ private static void SendVariantBulkCopy(object paramValue, string expectedTypeNa /// private static void SendVariantTvp(object paramValue, string expectedTypeName, string expectedBaseTypeName) { - string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); + string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); using SqlConnection connTvp = new(s_connStr); connTvp.Open(); 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 dc467e3775..9e3ed81af4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs @@ -240,7 +240,7 @@ public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLo public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string database = DataTestUtility.GetLongName($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); + string database = DataTestUtility.GetUniqueNameForSqlServer($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); var builder = new SqlConnectionStringBuilder(cnnString) { InitialCatalog = database, @@ -302,7 +302,7 @@ public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBase public void UpdateALockedTable(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string tableName = DataTestUtility.GetLongName("Region"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("Region"); string fieldName = "RegionDescription"; using (var cnn1 = new SqlConnection(cnnString)) 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 d6f1e39f77..c78c060677 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs @@ -58,7 +58,7 @@ public void ConnectionCancelRetryOpenInvalidCatalog(string cnnString, SqlRetryLo public void CreateDatabaseWhileTryingToConnect(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string database = DataTestUtility.GetLongName($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); + string database = DataTestUtility.GetUniqueNameForSqlServer($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); var builder = new SqlConnectionStringBuilder(cnnString) { InitialCatalog = database, diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs index a845710d50..72bab47869 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs @@ -41,7 +41,7 @@ public static void RunTest() private static SqlDecimal BulkCopySqlDecimalToTable(SqlDecimal decimalValue, int sourcePrecision, int sourceScale, int targetPrecision, int targetScale) { - string tableName = DataTestUtility.GetLongName("Table"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("Table"); string connectionString = DataTestUtility.TCPConnectionString; SqlDecimal resultValue; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs index 2a853d7ed4..823bc50a9d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests public class AzureDistributedTransaction { private static readonly string s_connectionString = DataTestUtility.TCPConnectionString; - private static readonly string s_tableName = DataTestUtility.GetLongName("Azure"); + private static readonly string s_tableName = DataTestUtility.GetUniqueNameForSqlServer("Azure"); private static readonly string s_createTableCmd = $"CREATE TABLE {s_tableName} (NAME NVARCHAR(40), AGE INT)"; private static readonly string s_sqlBulkCopyCmd = "SELECT * FROM(VALUES ('Fuller', 33), ('Davon', 49)) AS q (FirstName, Age)"; private static readonly int s_commandTimeout = 30; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs index f961521233..5ccda71fb9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs @@ -12,8 +12,8 @@ public class CopyWidenNullInexactNumerics { public static void Test(string sourceDatabaseConnectionString, string destinationDatabaseConnectionString) { - string sourceTableName = DataTestUtility.GetLongName("BCP_SRC"); - string destTableName = DataTestUtility.GetLongName("BCP_DST"); + string sourceTableName = DataTestUtility.GetUniqueNameForSqlServer("BCP_SRC"); + string destTableName = DataTestUtility.GetUniqueNameForSqlServer("BCP_DST"); // this test copies float and real inexact numeric types into decimal targets using bulk copy to check that the widening of the type succeeds. using (var sourceConnection = new SqlConnection(sourceDatabaseConnectionString)) 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 4a722dd409..4c3d594ad1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs @@ -28,7 +28,7 @@ public InitialDatabase() srcConstr = DataTestUtility.TCPConnectionString; Connection = new SqlConnection(srcConstr); - TableName = DataTestUtility.GetLongName("SqlBulkCopyTest_CopyStringToIntTest_"); + TableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBulkCopyTest_CopyStringToIntTest_"); InitialTable(Connection, TableName); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs index 21ff771ac0..8e38bee7c0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs @@ -11,7 +11,7 @@ public static class SqlCommandCompletedTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void VerifyStatmentCompletedCalled() { - string tableName = DataTestUtility.GetLongName("stmt"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("stmt"); using (var conn = new SqlConnection(s_connStr)) using (var cmd = conn.CreateCommand()) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs index 7f28a4a09a..26b11055c2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs @@ -15,8 +15,8 @@ public class SqlCommandSetTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void TestByteArrayParameters() { - string tableName = DataTestUtility.GetLongName("CMD"); - string procName = DataTestUtility.GetLongName("CMD"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("CMD"); + string procName = DataTestUtility.GetUniqueNameForSqlServer("CMD"); byte[] bArray = new byte[] { 1, 2, 3 }; using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs index 3bea30c6b8..09c369e11e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs @@ -221,7 +221,7 @@ private static string SetupFileStreamDB() fileStreamDir += "\\"; } - string dbName = DataTestUtility.GetShortName("FS", false); + string dbName = DataTestUtility.GetUniqueName("FS", false); string createDBQuery = @$"CREATE DATABASE [{dbName}] ON PRIMARY (NAME = PhotoLibrary_data, @@ -266,7 +266,7 @@ private static void DropFileStreamDb(string connString) private static string SetupTable(string connString) { // Generate random table name - string tempTable = DataTestUtility.GetLongName("fs"); + string tempTable = DataTestUtility.GetUniqueNameForSqlServer("fs"); // Create table string createTable = $"CREATE TABLE {tempTable} (EmployeeId INT NOT NULL PRIMARY KEY, Photo VARBINARY(MAX) FILESTREAM NULL, RowGuid UNIQUEIDENTIFIER NOT NULL ROWGUIDCOL UNIQUE DEFAULT NEWID() ) "; ExecuteNonQueryCommand(createTable, connString); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs index 8f697af02e..0e64b091b6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs @@ -434,7 +434,7 @@ private static string GetUdtName(Type udtClrType) [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void TestSqlServerTypesInsertAndRead() { - string tableName = DataTestUtility.GetLongName("Type"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("Type"); string allTypesSQL = @$" if not exists (select * from sysobjects where name='{tableName}' and xtype='U') Begin diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs index 5f17036b4f..90ceed4952 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs @@ -18,9 +18,9 @@ public void RunCopyTest() _connStr = (new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { InitialCatalog = DataTestUtility.UdtTestDbName }).ConnectionString; SqlConnection conn = new SqlConnection(_connStr); - string cities = DataTestUtility.GetLongName("UdtBulkCopy_cities"); - string customers = DataTestUtility.GetLongName("UdtBulkCopy_customers"); - string circles = DataTestUtility.GetLongName("UdtBulkCopy_circles"); + string cities = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_cities"); + string customers = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_customers"); + string circles = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_circles"); conn.Open(); try 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 ef38e43dda..1f07242ee3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -84,8 +84,8 @@ public void UDTParams_Binary() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_Invalid2() { - string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetLongName("UdtTest2"); + string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -143,8 +143,8 @@ public void UDTParams_Invalid() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_TypedNull() { - string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetLongName("UdtTest2_Customer"); + string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2_Customer"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -188,8 +188,8 @@ public void UDTParams_TypedNull() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_NullInput() { - string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetLongName("UdtTest2_Customer"); + string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2_Customer"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -232,8 +232,8 @@ public void UDTParams_NullInput() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_InputOutput() { - string spInsertCity = DataTestUtility.GetLongName("spUdtTest2_InsertCity"); - string tableName = DataTestUtility.GetLongName("UdtTest2"); + string spInsertCity = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCity"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2"); using (SqlConnection conn = new SqlConnection(_connStr)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs index 41f81b12e3..effecb35b3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs @@ -37,7 +37,7 @@ public static void CheckSupportUtf8ConnectionProperty() public static void UTF8databaseTest() { const string letters = @"!\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f€\u0081‚ƒ„…†‡ˆ‰Š‹Œ\u008dŽ\u008f\u0090‘’“”•–—˜™š›œ\u009džŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"; - string dbName = DataTestUtility.GetLongName("UTF8databaseTest", false); + string dbName = DataTestUtility.GetUniqueNameForSqlServer("UTF8databaseTest", false); string tblName = "Table1"; SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); From 1286cfe382659bc18d65f92632a6fa9119ae99d4 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Wed, 8 Oct 2025 19:07:40 -0300 Subject: [PATCH 64/76] [5.1] Stabilize CI Pipelines (#3599) * User Story 38467: Backport mac server name fix - Backported part of #3494 and #3591: - Added configurable test jobs timeout, defaulting to 90 minutes. - Reduced generated database names to 96 chars to try to fix macOS test failures. * User Story 38481: Fix unique db object name issues - Fixed the unique name generators to: - Keep max lengths to 30 and 96 characters respectively. - Ensure uniqueness at the start of the names. - Added link to database identifier syntax. * User Story 38481: Fix unique db object name issues - Removed DateOnly tests that aren't supported on 5.1. * Add CodeQL suppression for DefaultAzureCredential use in Production (#3542) - Adjusted CodeQL suppression to meet the strict requirements of where it may appear relative to the flagged code. - Adding catch for macOS socket error to log and ignore. * - Added back some blocks that were removed by cherry-picks. - Added console diagnostics to see when Enclave tables are dropped. * - Fixed typo in code that only gets compiled when building on Windows (eek!) * Tests | Remove hardcoded credentials from ManualTests (#3204) * Initial removal of CertificateUtility.CreateCertificate One test implied that DataTestUtility.AKVUrl would point to an RSA key which aligned with the certificate's private key. Switching this to dynamically generate the key in places. * Hotfix for Azure Key Vault tests * Removed hardcoded references to Azure Key Vault key * Removed hardcoded references to CertificateUtilityWin These were mostly related to generating CSP keys. * Code review changes * Reorder properties and constructors * Move AEConnectionStringProviderWithCspParameters to its own file * Tweak to the AKV token acquisition * Code review Redundant bracket, alphabetised the ManualTesting csproj * Update src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs Let's try @edwardneal's idea Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> * Update src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> * Fixes as per @edwardneal's suggestions * Fix as per @edwardneal's suggestion * Fix missing `new` Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> * Update src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyAzureKeyVault.cs Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> * Update src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyAzureKeyVault.cs Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> * Address comment that we don't need a CspParameters object as part of the test arguments * Move test arguments into property (the class was only used in a single location) * Cleanup test code * Tweak default provider discovery code to handle edge cases a bit better * Address comment regarding readonly member variables Apply long line chomping * Addressing the last of the comments. --------- Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> * Update test utilities target frameworks. Fix compilation issues. * Construct valid X500 distinguished name. * Print rsa key type for debugging. * Clean up net version ifdefs to fix certificate exportability. --------- Co-authored-by: Benjamin Russell Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> Co-authored-by: Malcolm Daigle --- .../jobs/run-tests-package-reference-job.yml | 10 + .../dotnet-sqlclient-signing-pipeline.yml | 7 + .../Data/SqlClient/SNI/SNITcpHandle.cs | 27 +- .../ActiveDirectoryAuthenticationProvider.cs | 23 +- .../ManualTests/AlwaysEncrypted/AKVTests.cs | 10 +- .../AlwaysEncrypted/AKVUnitTests.cs | 77 ++--- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 10 +- .../AlwaysEncrypted/ConversionTests.cs | 29 +- .../AlwaysEncrypted/CspProviderExt.cs | 246 +++++---------- .../EnclaveAzureDatabaseTests.cs | 6 +- .../AlwaysEncrypted/ExceptionTestAKVStore.cs | 62 ++-- .../TestFixtures/AzureKeyVaultKeyFixture.cs | 19 ++ .../TestFixtures/SQLSetupStrategy.cs | 51 +-- .../SQLSetupStrategyAzureKeyVault.cs | 65 +++- .../SQLSetupStrategyCertStoreProvider.cs | 7 +- .../TestFixtures/SQLSetupStrategyCspExt.cs | 50 --- .../SQLSetupStrategyCspProvider.cs | 77 +++++ .../TestFixtures/Setup/CertificateUtility.cs | 89 ------ .../Setup/CertificateUtilityWin.cs | 264 ---------------- .../TestFixtures/Setup/Table.cs | 3 + .../TestTrustedMasterKeyPaths.cs | 27 +- .../ManualTests/DataCommon/DataTestUtility.cs | 209 ++++++++++--- .../SqlClientCustomTokenCredential.cs | 9 +- ....Data.SqlClient.ManualTesting.Tests.csproj | 4 +- .../ProviderAgnostic/ReaderTest/ReaderTest.cs | 6 +- .../SQL/AdapterTest/AdapterTest.cs | 10 +- .../SQL/ConnectivityTests/ConnectivityTest.cs | 5 +- .../DataClassificationTest.cs | 6 +- .../SQL/DataReaderTest/DataReaderTest.cs | 8 +- .../SQL/DataStreamTest/DataStreamTest.cs | 10 +- .../SQL/ParameterTest/DateTimeVariantTest.cs | 52 +-- .../SQL/ParameterTest/ParametersTest.cs | 20 +- .../ParameterTest/SqlAdapterUpdateBatch.cs | 2 +- .../SQL/ParameterTest/SqlVariantParam.cs | 4 +- .../RetryLogic/SqlCommandReliabilityTest.cs | 4 +- .../SqlConnectionReliabilityTest.cs | 2 +- .../AdjustPrecScaleForBulkCopy.cs | 2 +- .../AzureDistributedTransaction.cs | 2 +- .../CopyWidenNullInexactNumerics.cs | 4 +- .../DataConversionErrorMessageTest.cs | 2 +- .../SQL/SqlCommand/SqlCommandCompletedTest.cs | 2 +- .../SQL/SqlCommand/SqlCommandSetTest.cs | 4 +- .../SqlFileStreamTest/SqlFileStreamTest.cs | 4 +- .../SQL/UdtTest/SqlServerTypesTest.cs | 2 +- .../SQL/UdtTest/UdtBulkCopyTest.cs | 6 +- .../tests/ManualTests/SQL/UdtTest/UdtTest2.cs | 16 +- .../SQL/Utf8SupportTest/Utf8SupportTest.cs | 2 +- .../Fixtures/AzureKeyVaultKeyFixtureBase.cs | 63 ++++ .../Fixtures/CertificateFixtureBase.cs | 296 ++++++++++++++++++ .../ColumnMasterKeyCertificateFixture.cs | 29 ++ .../Fixtures/CspCertificateFixture.cs | 47 +++ ...rosoft.Data.SqlClient.TestUtilities.csproj | 7 +- tools/props/Versions.props | 1 + 53 files changed, 1150 insertions(+), 849 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/AzureKeyVaultKeyFixture.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspProvider.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtilityWin.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/AzureKeyVaultKeyFixtureBase.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CertificateFixtureBase.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/ColumnMasterKeyCertificateFixture.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CspCertificateFixture.cs diff --git a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml index a9fcd12720..9973e362e7 100644 --- a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml +++ b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml @@ -17,11 +17,21 @@ parameters: type: string default: empty + # The timeout, in minutes, for this job. + - name: timeout + type: string + default: 90 + jobs: - job: run_tests_package_reference displayName: 'Run tests with package reference' ${{ if ne(parameters.dependsOn, 'empty')}}: dependsOn: '${{parameters.dependsOn }}' + + # Some of our tests take longer than the default 60 minutes to run on some + # OSes and configurations. + timeoutInMinutes: ${{ parameters.timeout }} + pool: type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs isCustom: true diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml index 299a4085fa..b422fa7836 100644 --- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml @@ -65,6 +65,12 @@ parameters: # parameters are shown up in ADO UI in a build queue time - NonOfficial - Official +# The timeout, in minutes, for each test job. +- name: testsTimeout + displayName: 'Tests timeout (in minutes)' + type: string + default: 90 + variables: - template: /eng/pipelines/libraries/variables.yml@self - name: packageFolderName @@ -172,6 +178,7 @@ extends: - template: eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml@self parameters: packageFolderName: $(packageFolderName) + timeout: ${{ parameters.testsTimeout }} downloadPackageStep: download: current artifact: $(packageFolderName) 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 b8e38978b9..9537eaf1c6 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 @@ -851,9 +851,30 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) } finally { - // Reset the socket timeout to Timeout.Infinite after the receive operation is done - // to avoid blocking the thread in case of a timeout error. - _socket.ReceiveTimeout = Timeout.Infinite; + const int resetTimeout = Timeout.Infinite; + + try + { + // Reset the socket timeout to Timeout.Infinite after + // the receive operation is done to avoid blocking the + // thread in case of a timeout error. + _socket.ReceiveTimeout = resetTimeout; + + } + catch (SocketException ex) + { + // We sometimes see setting the ReceiveTimeout fail + // on macOS. There's isn't much we can do about it + // though, so just log and move on. + SqlClientEventSource.Log.TrySNITraceEvent( + nameof(SNITCPHandle), + EventType.ERR, + "Connection Id {0}, Failed to reset socket " + + "receive timeout to {1}: {2}", + _connectionId, + resetTimeout, + ex.Message); + } } } } 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 73fce50774..d69cbe06ab 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -587,7 +587,28 @@ private static TokenCredentialData CreateTokenCredentialInstance(TokenCredential defaultAzureCredentialOptions.WorkloadIdentityClientId = tokenCredentialKey._clientId; } - return new TokenCredentialData(new DefaultAzureCredential(defaultAzureCredentialOptions), GetHash(secret)); + // SqlClient is a library and provides support to acquire access + // token using 'DefaultAzureCredential' on user demand when they + // specify 'Authentication = Active Directory Default' in + // connection string. + // + // Default Azure Credential is instantiated by the calling + // application when using "Active Directory Default" + // authentication code to connect to Azure SQL instance. + // SqlClient is a library, doesn't instantiate the credential + // without running application instructions. + // + // Note that CodeQL suppression support can only detect + // suppression comments that appear immediately above the + // flagged statement, or appended to the end of the statement. + // Multi-line justifications are not supported. + // + // https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/codeql/codeql-semmle#guidance-on-suppressions + // + // CodeQL [SM05137] See above for justification. + DefaultAzureCredential cred = new(defaultAzureCredentialOptions); + + return new TokenCredentialData(cred, GetHash(secret)); } TokenCredentialOptions tokenCredentialOptions = new() { AuthorityHost = new Uri(tokenCredentialKey._authority) }; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVTests.cs index 3f2b7a83fa..ab4f38f882 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVTests.cs @@ -62,15 +62,15 @@ public void TestEncryptDecryptWithAKV() [PlatformSpecific(TestPlatforms.Windows)] public void TestRoundTripWithAKVAndCertStoreProvider() { - using SQLSetupStrategyCertStoreProvider certStoreFixture = new (); + SqlColumnEncryptionCertificateStoreProvider certStoreProvider = new SqlColumnEncryptionCertificateStoreProvider(); byte[] plainTextColumnEncryptionKey = ColumnEncryptionKey.GenerateRandomBytes(ColumnEncryptionKey.KeySizeInBytes); - byte[] encryptedColumnEncryptionKeyUsingAKV = _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, @"RSA_OAEP", plainTextColumnEncryptionKey); - byte[] columnEncryptionKeyReturnedAKV2Cert = certStoreFixture.CertStoreProvider.DecryptColumnEncryptionKey(certStoreFixture.CspColumnMasterKey.KeyPath, @"RSA_OAEP", encryptedColumnEncryptionKeyUsingAKV); + byte[] encryptedColumnEncryptionKeyUsingAKV = _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(_fixture.AkvKeyUrl, @"RSA_OAEP", plainTextColumnEncryptionKey); + byte[] columnEncryptionKeyReturnedAKV2Cert = certStoreProvider.DecryptColumnEncryptionKey(_fixture.ColumnMasterKeyPath, @"RSA_OAEP", encryptedColumnEncryptionKeyUsingAKV); Assert.True(plainTextColumnEncryptionKey.SequenceEqual(columnEncryptionKeyReturnedAKV2Cert), @"Roundtrip failed"); // Try the opposite. - byte[] encryptedColumnEncryptionKeyUsingCert = certStoreFixture.CertStoreProvider.EncryptColumnEncryptionKey(certStoreFixture.CspColumnMasterKey.KeyPath, @"RSA_OAEP", plainTextColumnEncryptionKey); - byte[] columnEncryptionKeyReturnedCert2AKV = _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, @"RSA_OAEP", encryptedColumnEncryptionKeyUsingCert); + byte[] encryptedColumnEncryptionKeyUsingCert = certStoreProvider.EncryptColumnEncryptionKey(_fixture.ColumnMasterKeyPath, @"RSA_OAEP", plainTextColumnEncryptionKey); + byte[] columnEncryptionKeyReturnedCert2AKV = _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, @"RSA_OAEP", encryptedColumnEncryptionKeyUsingCert); Assert.True(plainTextColumnEncryptionKey.SequenceEqual(columnEncryptionKeyReturnedCert2AKV), @"Roundtrip failed"); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs index 893c50799b..ed7061cf65 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs @@ -14,13 +14,20 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { - public static class AKVUnitTests + public class AKVUnitTests : IClassFixture { 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"; + private readonly AzureKeyVaultKeyFixture _fixture; + + public AKVUnitTests(AzureKeyVaultKeyFixture fixture) + { + _fixture = fixture; + } + private static void ValidateAKVTraces(List eventData, Guid threadActivityId) { Assert.NotNull(eventData); @@ -64,36 +71,36 @@ private static void ValidateAKVTraces(List eventData, Gui } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] - public static void LegacyAuthenticationCallbackTest() + public void LegacyAuthenticationCallbackTest() { Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid(); using DataTestUtility.AKVEventListener AKVListener = new(); // SqlClientCustomTokenCredential implements legacy authentication callback to request access token at client-side. SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential()); - byte[] encryptedCek = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey); - byte[] decryptedCek = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCek); + byte[] encryptedCek = akvProvider.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, EncryptionAlgorithm, s_columnEncryptionKey); + byte[] decryptedCek = akvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, EncryptionAlgorithm, encryptedCek); Assert.Equal(s_columnEncryptionKey, decryptedCek); ValidateAKVTraces(AKVListener.EventData, activityId); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] - public static void TokenCredentialTest() + public void TokenCredentialTest() { Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid(); using DataTestUtility.AKVEventListener AKVListener = new(); SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential()); - byte[] encryptedCek = akvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey); - byte[] decryptedCek = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCek); + byte[] encryptedCek = akvProvider.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, EncryptionAlgorithm, s_columnEncryptionKey); + byte[] decryptedCek = akvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, EncryptionAlgorithm, encryptedCek); Assert.Equal(s_columnEncryptionKey, decryptedCek); ValidateAKVTraces(AKVListener.EventData, activityId); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] - public static void TokenCredentialRotationTest() + public void TokenCredentialRotationTest() { Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid(); using DataTestUtility.AKVEventListener AKVListener = new(); @@ -103,19 +110,19 @@ public static void TokenCredentialRotationTest() SqlColumnEncryptionAzureKeyVaultProvider newAkvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(DataTestUtility.GetTokenCredential()); - byte[] encryptedCekWithNewProvider = newAkvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey); - byte[] decryptedCekWithOldProvider = oldAkvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCekWithNewProvider); + byte[] encryptedCekWithNewProvider = newAkvProvider.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, EncryptionAlgorithm, s_columnEncryptionKey); + byte[] decryptedCekWithOldProvider = oldAkvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, EncryptionAlgorithm, encryptedCekWithNewProvider); Assert.Equal(s_columnEncryptionKey, decryptedCekWithOldProvider); - byte[] encryptedCekWithOldProvider = oldAkvProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, s_columnEncryptionKey); - byte[] decryptedCekWithNewProvider = newAkvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, EncryptionAlgorithm, encryptedCekWithOldProvider); + byte[] encryptedCekWithOldProvider = oldAkvProvider.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, EncryptionAlgorithm, s_columnEncryptionKey); + byte[] decryptedCekWithNewProvider = newAkvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, EncryptionAlgorithm, encryptedCekWithOldProvider); Assert.Equal(s_columnEncryptionKey, decryptedCekWithNewProvider); ValidateAKVTraces(AKVListener.EventData, activityId); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] - public static void ReturnSpecifiedVersionOfKeyWhenItIsNotTheMostRecentVersion() + public void ReturnSpecifiedVersionOfKeyWhenItIsNotTheMostRecentVersion() { Uri keyPathUri = new Uri(DataTestUtility.AKVOriginalUrl); Uri vaultUri = new Uri(keyPathUri.GetLeftPart(UriPartial.Authority)); @@ -161,7 +168,7 @@ public static void ThrowWhenUrlHasLessThanThreeSegments() } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] - public static void DecryptedCekIsCachedDuringDecryption() + public void DecryptedCekIsCachedDuringDecryption() { Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid(); using DataTestUtility.AKVEventListener AKVListener = new(); @@ -170,23 +177,23 @@ public static void DecryptedCekIsCachedDuringDecryption() 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[] encryptedKey1 = akvProvider.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", plaintextKey1); + byte[] encryptedKey2 = akvProvider.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", plaintextKey2); + byte[] encryptedKey3 = akvProvider.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", plaintextKey3); - byte[] decryptedKey1 = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey1); + byte[] decryptedKey1 = akvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", encryptedKey1); Assert.Equal(1, GetCacheCount(cekCacheName, akvProvider)); Assert.Equal(plaintextKey1, decryptedKey1); - decryptedKey1 = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey1); + decryptedKey1 = akvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", encryptedKey1); Assert.Equal(1, GetCacheCount(cekCacheName, akvProvider)); Assert.Equal(plaintextKey1, decryptedKey1); - byte[] decryptedKey2 = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey2); + byte[] decryptedKey2 = akvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", encryptedKey2); Assert.Equal(2, GetCacheCount(cekCacheName, akvProvider)); Assert.Equal(plaintextKey2, decryptedKey2); - byte[] decryptedKey3 = akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey3); + byte[] decryptedKey3 = akvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", encryptedKey3); Assert.Equal(3, GetCacheCount(cekCacheName, akvProvider)); Assert.Equal(plaintextKey3, decryptedKey3); @@ -194,33 +201,33 @@ public static void DecryptedCekIsCachedDuringDecryption() } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] - public static void SignatureVerificationResultIsCachedDuringVerification() + public void SignatureVerificationResultIsCachedDuringVerification() { Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid(); using DataTestUtility.AKVEventListener AKVListener = new(); 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); + byte[] signature = akvProvider.SignColumnMasterKeyMetadata(_fixture.GeneratedKeyUri, true); + byte[] signature2 = akvProvider.SignColumnMasterKeyMetadata(_fixture.GeneratedKeyUri, true); + byte[] signatureWithoutEnclave = akvProvider.SignColumnMasterKeyMetadata(_fixture.GeneratedKeyUri, false); - Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, true, signature)); + Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(_fixture.GeneratedKeyUri, true, signature)); Assert.Equal(1, GetCacheCount(signatureVerificationResultCacheName, akvProvider)); - Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, true, signature)); + Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(_fixture.GeneratedKeyUri, true, signature)); Assert.Equal(1, GetCacheCount(signatureVerificationResultCacheName, akvProvider)); - Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, true, signature2)); + Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(_fixture.GeneratedKeyUri, true, signature2)); Assert.Equal(1, GetCacheCount(signatureVerificationResultCacheName, akvProvider)); - Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, false, signatureWithoutEnclave)); + Assert.True(akvProvider.VerifyColumnMasterKeyMetadata(_fixture.GeneratedKeyUri, false, signatureWithoutEnclave)); Assert.Equal(2, GetCacheCount(signatureVerificationResultCacheName, akvProvider)); ValidateAKVTraces(AKVListener.EventData, activityId); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] - public static void CekCacheEntryIsEvictedAfterTtlExpires() + public void CekCacheEntryIsEvictedAfterTtlExpires() { Guid activityId = Trace.CorrelationManager.ActivityId = Guid.NewGuid(); using DataTestUtility.AKVEventListener AKVListener = new(); @@ -228,9 +235,9 @@ 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); + byte[] encryptedKey = akvProvider.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", plaintextKey); - akvProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey); + akvProvider.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", encryptedKey); Assert.True(CekCacheContainsKey(encryptedKey, akvProvider)); Assert.Equal(1, GetCacheCount(cekCacheName, akvProvider)); @@ -242,7 +249,7 @@ public static void CekCacheEntryIsEvictedAfterTtlExpires() } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] - public static void CekCacheShouldBeDisabledWhenCustomProviderIsRegisteredGlobally() + public void CekCacheShouldBeDisabledWhenCustomProviderIsRegisteredGlobally() { if (SQLSetupStrategyAzureKeyVault.IsAKVProviderRegistered) { @@ -255,9 +262,9 @@ public static void CekCacheShouldBeDisabledWhenCustomProviderIsRegisteredGloball SqlColumnEncryptionAzureKeyVaultProvider akvProviderInGlobalCache = globalProviders["AZURE_KEY_VAULT"] as SqlColumnEncryptionAzureKeyVaultProvider; byte[] plaintextKey = { 1, 2, 3 }; - byte[] encryptedKey = akvProviderInGlobalCache.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", plaintextKey); + byte[] encryptedKey = akvProviderInGlobalCache.EncryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", plaintextKey); - akvProviderInGlobalCache.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_OAEP", encryptedKey); + akvProviderInGlobalCache.DecryptColumnEncryptionKey(_fixture.GeneratedKeyUri, "RSA_OAEP", encryptedKey); Assert.Equal(0, GetCacheCount(cekCacheName, akvProviderInGlobalCache)); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 8e7458f02e..b987bdbc99 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -148,8 +148,8 @@ public void SqlParameterProperties(string connection) const string firstColumnName = @"firstColumn"; const string secondColumnName = @"secondColumn"; const string thirdColumnName = @"thirdColumn"; - string inputProcedureName = DataTestUtility.GetUniqueName("InputProc").ToString(); - string outputProcedureName = DataTestUtility.GetUniqueName("OutputProc").ToString(); + string inputProcedureName = DataTestUtility.GetShortName("InputProc").ToString(); + string outputProcedureName = DataTestUtility.GetShortName("OutputProc").ToString(); const int charColumnSize = 100; const int decimalColumnPrecision = 10; const int decimalColumnScale = 4; @@ -694,7 +694,7 @@ public void TestExecuteReader(string connection) [ClassData(typeof(AEConnectionStringProvider))] public async void TestExecuteReaderAsyncWithLargeQuery(string connectionString) { - string randomName = DataTestUtility.GetUniqueName(Guid.NewGuid().ToString().Replace("-", ""), false); + string randomName = DataTestUtility.GetShortName(Guid.NewGuid().ToString().Replace("-", ""), false); if (randomName.Length > 50) { randomName = randomName.Substring(0, 50); @@ -878,8 +878,8 @@ public void TestEnclaveStoredProceduresWithAndWithoutParameters(string connectio using SqlCommand sqlCommand = new("", sqlConnection, transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled); - string procWithoutParams = DataTestUtility.GetUniqueName("EnclaveWithoutParams", withBracket: false); - string procWithParam = DataTestUtility.GetUniqueName("EnclaveWithParams", withBracket: false); + string procWithoutParams = DataTestUtility.GetShortName("EnclaveWithoutParams", withBracket: false); + string procWithParam = DataTestUtility.GetShortName("EnclaveWithParams", withBracket: false); try { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs index 04c661902d..a7be11add4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs @@ -14,11 +14,12 @@ using System.Security.Cryptography.X509Certificates; using Xunit; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; +using Microsoft.Data.SqlClient.TestUtilities.Fixtures; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { [PlatformSpecific(TestPlatforms.Windows)] - public sealed class ConversionTests : IDisposable + public sealed class ConversionTests : IDisposable, IClassFixture { private const string IdentityColumnName = "IdentityColumn"; @@ -29,8 +30,6 @@ public sealed class ConversionTests : IDisposable private const decimal SmallMoneyMinValue = -214748.3648M; private const int MaxLength = 10000; private int NumberOfRows = DataTestUtility.EnclaveEnabled ? 10 : 100; - private static X509Certificate2 certificate; - private ColumnMasterKey columnMasterKey; private ColumnEncryptionKey columnEncryptionKey; private SqlColumnEncryptionCertificateStoreProvider certStoreProvider = new SqlColumnEncryptionCertificateStoreProvider(); private List _databaseObjects = new List(); @@ -54,18 +53,20 @@ public ColumnMetaData(SqlDbType columnType, int columnSize, int precision, int s public bool UseMax { get; set; } } - public ConversionTests() + public ConversionTests(ColumnMasterKeyCertificateFixture fixture) { - if (certificate == null) - { - certificate = CertificateUtility.CreateCertificate(); - } - columnMasterKey = new CspColumnMasterKey(DatabaseHelper.GenerateUniqueName("CMK"), certificate.Thumbprint, certStoreProvider, DataTestUtility.EnclaveEnabled); - _databaseObjects.Add(columnMasterKey); - - columnEncryptionKey = new ColumnEncryptionKey(DatabaseHelper.GenerateUniqueName("CEK"), - columnMasterKey, - certStoreProvider); + X509Certificate2 certificate = fixture.ColumnMasterKeyCertificate; + ColumnMasterKey columnMasterKey1 = new CspColumnMasterKey( + DatabaseHelper.GenerateUniqueName("CMK"), + certificate.Thumbprint, + certStoreProvider, + DataTestUtility.EnclaveEnabled); + _databaseObjects.Add(columnMasterKey1); + + columnEncryptionKey = new ColumnEncryptionKey( + DatabaseHelper.GenerateUniqueName("CEK"), + columnMasterKey1, + certStoreProvider); _databaseObjects.Add(columnEncryptionKey); foreach (string connectionStr in DataTestUtility.AEConnStringsSetup) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs index 49071def2e..c93b770958 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs @@ -2,14 +2,16 @@ // 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.IO; using System.Linq; using System.Security.Cryptography.X509Certificates; -using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; using Xunit; -using System.Collections.Generic; -using static Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.CertificateUtilityWin; -#if NET6_0_OR_GREATER +using System.Security.Cryptography; +using Microsoft.Data.SqlClient.TestUtilities.Fixtures; +using Microsoft.Win32; + +#if NET using System.Runtime.Versioning; #endif @@ -25,185 +27,97 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted [PlatformSpecific(TestPlatforms.Windows)] public class CspProviderExt { - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] - [ClassData(typeof(AEConnectionStringProvider))] - public void TestKeysFromCertificatesCreatedWithMultipleCryptoProviders(string connectionString) - { - const string providersRegistryKeyPath = @"SOFTWARE\Microsoft\Cryptography\Defaults\Provider"; - Microsoft.Win32.RegistryKey defaultCryptoProvidersRegistryKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(providersRegistryKeyPath); - - foreach (string subKeyName in defaultCryptoProvidersRegistryKey.GetSubKeyNames()) - { - // NOTE: RSACryptoServiceProvider.SignData() fails for other providers when testing locally - if (!subKeyName.Contains(@"RSA and AES")) - { - Console.WriteLine(@"INFO: Skipping Certificate creation for {0}.", subKeyName); - continue; - } - string providerName; - string providerType; - string certificateName; - using (Microsoft.Win32.RegistryKey providerKey = defaultCryptoProvidersRegistryKey.OpenSubKey(subKeyName)) - { - // Get Provider Name and its type - providerName = providerKey.Name.Substring(providerKey.Name.LastIndexOf(@"\") + 1); - providerType = providerKey.GetValue(@"Type").ToString(); - - // Create a certificate from that provider - certificateName = string.Format(@"AETest - {0}", providerName); - } - - var extensions = new List>(); - CertificateOption certOption = new() - { - Subject = certificateName, - KeyExportPolicy = "Exportable", - CertificateStoreLocation = $"{StoreLocation.CurrentUser}\\My", - Provider = providerName, - Type = providerType, - }; - CreateCertificate(certOption); - SQLSetupStrategyCspExt sqlSetupStrategyCsp = null; - try - { - if (false == CertificateUtilityWin.CertificateExists(certificateName, StoreLocation.CurrentUser)) - { - Console.WriteLine(@"INFO: Certificate creation for provider {0} failed so skipping it.", providerName); - continue; - } - - X509Certificate2 cert = CertificateUtilityWin.GetCertificate(certificateName, StoreLocation.CurrentUser); - string cspPath = CertificateUtilityWin.GetCspPathFromCertificate(cert); - - if (string.IsNullOrEmpty(cspPath)) - { - Console.WriteLine(@"INFO: Certificate provider {0} is not a csp provider so skipping it.", providerName); - continue; - } - - Console.WriteLine("CSP path is {0}", cspPath); - - sqlSetupStrategyCsp = new SQLSetupStrategyCspExt(cspPath); - string tableName = sqlSetupStrategyCsp.CspProviderTable.Name; - - using SqlConnection sqlConn = new(connectionString); - sqlConn.Open(); - - Table.DeleteData(tableName, sqlConn); - - // insert 1 row data - Customer customer = new Customer(45, "Microsoft", "Corporation"); - - DatabaseHelper.InsertCustomerData(sqlConn, null, tableName, customer); - - // Test INPUT parameter on an encrypted parameter - using SqlCommand sqlCommand = new(@$"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @firstName", - sqlConn, null, SqlCommandColumnEncryptionSetting.Enabled); - SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); - customerFirstParam.Direction = System.Data.ParameterDirection.Input; - - using SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(); - ValidateResultSet(sqlDataReader); - Console.WriteLine(@"INFO: Successfully validated using a certificate using provider:{0}", providerName); - } - finally - { - CertificateUtilityWin.RemoveCertificate(certificateName, StoreLocation.CurrentUser); - // clean up database resources - sqlSetupStrategyCsp?.Dispose(); - } - - } - } - // [Fact(Skip="Run this in non-parallel mode")] or [ConditionalFact()] [Fact(Skip = "Failing in TCE")] - public void TestRoundTripWithCSPAndCertStoreProvider() + public void TestRoundTripWithCspAndCertStoreProvider() { - const string providerName = "Microsoft Enhanced RSA and AES Cryptographic Provider"; - string providerType = "24"; + using CspCertificateFixture cspCertificateFixture = new CspCertificateFixture(); - string certificateName = string.Format(@"AETest - {0}", providerName); - CertificateOption options = new() - { - Subject = certificateName, - CertificateStoreLocation = StoreLocation.CurrentUser.ToString(), - Provider = providerName, - Type = providerType - }; - CertificateUtilityWin.CreateCertificate(options); - try - { - X509Certificate2 cert = CertificateUtilityWin.GetCertificate(certificateName, StoreLocation.CurrentUser); - string cspPath = CertificateUtilityWin.GetCspPathFromCertificate(cert); - string certificatePath = String.Concat(@"CurrentUser/my/", cert.Thumbprint); + X509Certificate2 cert = cspCertificateFixture.CspCertificate; + string cspPath = cspCertificateFixture.CspKeyPath; + string certificatePath = cspCertificateFixture.CspCertificatePath; - SqlColumnEncryptionCertificateStoreProvider certProvider = new SqlColumnEncryptionCertificateStoreProvider(); - SqlColumnEncryptionCspProvider cspProvider = new SqlColumnEncryptionCspProvider(); - byte[] columnEncryptionKey = DatabaseHelper.GenerateRandomBytes(32); + SqlColumnEncryptionCertificateStoreProvider certProvider = new SqlColumnEncryptionCertificateStoreProvider(); + SqlColumnEncryptionCspProvider cspProvider = new SqlColumnEncryptionCspProvider(); + byte[] columnEncryptionKey = DatabaseHelper.GenerateRandomBytes(32); - byte[] encryptedColumnEncryptionKeyUsingCert = certProvider.EncryptColumnEncryptionKey(certificatePath, @"RSA_OAEP", columnEncryptionKey); - byte[] columnEncryptionKeyReturnedCert2CSP = cspProvider.DecryptColumnEncryptionKey(cspPath, @"RSA_OAEP", encryptedColumnEncryptionKeyUsingCert); - Assert.True(columnEncryptionKey.SequenceEqual(columnEncryptionKeyReturnedCert2CSP)); + byte[] encryptedColumnEncryptionKeyUsingCert = certProvider.EncryptColumnEncryptionKey(certificatePath, @"RSA_OAEP", columnEncryptionKey); + byte[] columnEncryptionKeyReturnedCert2CSP = cspProvider.DecryptColumnEncryptionKey(cspPath, @"RSA_OAEP", encryptedColumnEncryptionKeyUsingCert); + Assert.True(columnEncryptionKey.SequenceEqual(columnEncryptionKeyReturnedCert2CSP)); - byte[] encryptedColumnEncryptionKeyUsingCSP = cspProvider.EncryptColumnEncryptionKey(cspPath, @"RSA_OAEP", columnEncryptionKey); - byte[] columnEncryptionKeyReturnedCSP2Cert = certProvider.DecryptColumnEncryptionKey(certificatePath, @"RSA_OAEP", encryptedColumnEncryptionKeyUsingCSP); - Assert.True(columnEncryptionKey.SequenceEqual(columnEncryptionKeyReturnedCSP2Cert)); - - } - finally - { - CertificateUtilityWin.RemoveCertificate(certificateName, StoreLocation.CurrentUser); - } + byte[] encryptedColumnEncryptionKeyUsingCSP = cspProvider.EncryptColumnEncryptionKey(cspPath, @"RSA_OAEP", columnEncryptionKey); + byte[] columnEncryptionKeyReturnedCSP2Cert = certProvider.DecryptColumnEncryptionKey(certificatePath, @"RSA_OAEP", encryptedColumnEncryptionKeyUsingCSP); + Assert.True(columnEncryptionKey.SequenceEqual(columnEncryptionKeyReturnedCSP2Cert)); } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] - [ClassData(typeof(AEConnectionStringProvider))] - public void TestEncryptDecryptWithCSP(string connectionString) + [MemberData(nameof(TestEncryptDecryptWithCsp_Data))] + public void TestEncryptDecryptWithCsp(string connectionString, string providerName, int providerType) { - string providerName = @"Microsoft Enhanced RSA and AES Cryptographic Provider"; - string keyIdentifier = DataTestUtility.GetUniqueNameForSqlServer("CSP"); + string keyIdentifier = DataTestUtility.GetLongName("CSP"); + CspParameters namedCspParameters = new CspParameters(providerType, providerName, keyIdentifier); + using SQLSetupStrategyCspProvider sqlSetupStrategyCsp = new SQLSetupStrategyCspProvider(namedCspParameters); + + using SqlConnection sqlConn = new(connectionString); + sqlConn.Open(); + + // Test INPUT parameter on an encrypted parameter + string commandText = @$"SELECT CustomerId, FirstName, LastName " + + @$"FROM [{sqlSetupStrategyCsp.ApiTestTable.Name}] " + + @$"WHERE FirstName = @firstName"; + using SqlCommand sqlCommand = new(commandText, sqlConn, null, SqlCommandColumnEncryptionSetting.Enabled); + + SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); + customerFirstParam.Direction = System.Data.ParameterDirection.Input; + + using SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(); + ValidateResultSet(sqlDataReader); + } - try + public static IEnumerable TestEncryptDecryptWithCsp_Data + { + get { - CertificateUtilityWin.RSAPersistKeyInCsp(providerName, keyIdentifier); - string cspPath = String.Concat(providerName, @"/", keyIdentifier); - - SQLSetupStrategyCspExt sqlSetupStrategyCsp = new SQLSetupStrategyCspExt(cspPath); - string tableName = sqlSetupStrategyCsp.CspProviderTable.Name; - - try + const string providerRegistryKeyPath = @"SOFTWARE\Microsoft\Cryptography\Defaults\Provider"; + using RegistryKey defaultProviderRegistryKey = Registry.LocalMachine.OpenSubKey(providerRegistryKeyPath); + if (defaultProviderRegistryKey is null) { - using SqlConnection sqlConn = new(connectionString); - sqlConn.Open(); - - Table.DeleteData(tableName, sqlConn); - - // insert 1 row data - Customer customer = new Customer(45, "Microsoft", "Corporation"); - - DatabaseHelper.InsertCustomerData(sqlConn, null, tableName, customer); - - // Test INPUT parameter on an encrypted parameter - using SqlCommand sqlCommand = new(@$"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @firstName", - sqlConn, null, SqlCommandColumnEncryptionSetting.Enabled); - SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); - Console.WriteLine(@"Exception: {0}"); - customerFirstParam.Direction = System.Data.ParameterDirection.Input; - - using SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(); - ValidateResultSet(sqlDataReader); + // No test cases can be generated if the registry key doesn't exist. + yield break; } - finally + + foreach (string subKeyName in defaultProviderRegistryKey.GetSubKeyNames()) { - // clean up database resources - sqlSetupStrategyCsp.Dispose(); + // Skip inappropriate providers + if (!subKeyName.Contains(@"RSA and AES")) + { + continue; + } + + // Open the provider + using RegistryKey providerKey = defaultProviderRegistryKey.OpenSubKey(subKeyName); + if (providerKey is null) + { + continue; + } + + // Read provider name + string providerName = Path.GetFileName(providerKey.Name); + + // Read provider type + object providerTypeValue = providerKey.GetValue(@"Type"); + if (providerTypeValue is not int providerType) + { + continue; + } + + // Combine with AE connection strings + foreach (string aeConnectionString in DataTestUtility.AEConnStrings) + { + yield return new object[] { aeConnectionString, providerName, providerType }; + } } } - finally - { - CertificateUtilityWin.RSADeleteKeyInCsp(providerName, keyIdentifier); - } } /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs index c372e39bca..536be83a3e 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 sealed class EnclaveAzureDatabaseTests : IDisposable + public sealed class EnclaveAzureDatabaseTests : IDisposable, IClassFixture { private ColumnMasterKey akvColumnMasterKey; private ColumnEncryptionKey akvColumnEncryptionKey; @@ -22,7 +22,7 @@ public sealed class EnclaveAzureDatabaseTests : IDisposable private List databaseObjects = new List(); private List connStrings = new List(); - public EnclaveAzureDatabaseTests() + public EnclaveAzureDatabaseTests(AzureKeyVaultKeyFixture keyVaultKeyFixture) { if (DataTestUtility.IsEnclaveAzureDatabaseSetup()) { @@ -32,7 +32,7 @@ public EnclaveAzureDatabaseTests() SQLSetupStrategyAzureKeyVault.RegisterGlobalProviders(sqlColumnEncryptionAzureKeyVaultProvider); } - akvColumnMasterKey = new AkvColumnMasterKey(DatabaseHelper.GenerateUniqueName("AKVCMK"), akvUrl: DataTestUtility.AKVUrl, sqlColumnEncryptionAzureKeyVaultProvider, DataTestUtility.EnclaveEnabled); + akvColumnMasterKey = new AkvColumnMasterKey(DatabaseHelper.GenerateUniqueName("AKVCMK"), akvUrl: keyVaultKeyFixture.GeneratedKeyUri, sqlColumnEncryptionAzureKeyVaultProvider, DataTestUtility.EnclaveEnabled); databaseObjects.Add(akvColumnMasterKey); akvColumnEncryptionKey = new ColumnEncryptionKey(DatabaseHelper.GenerateUniqueName("AKVCEK"), diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs index 6cb20a4351..2465633a03 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs @@ -15,16 +15,16 @@ public class ExceptionTestAKVStore : IClassFixture(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, BadMasterKeyEncAlgo, cek)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, BadMasterKeyEncAlgo, cek)); Assert.Matches($@"Invalid key encryption algorithm specified: 'BadMasterKeyAlgorithm'. Expected value: 'RSA_OAEP' or 'RSA-OAEP'.\s+\(?Parameter (name: )?'?encryptionAlgorithm('\))?", ex1.Message); - Exception ex2 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, BadMasterKeyEncAlgo, cek)); + Exception ex2 = Assert.Throws(() => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(_fixture.AkvKeyUrl, BadMasterKeyEncAlgo, cek)); Assert.Matches($@"Invalid key encryption algorithm specified: 'BadMasterKeyAlgorithm'. Expected value: 'RSA_OAEP' or 'RSA-OAEP'.\s+\(?Parameter (name: )?'?encryptionAlgorithm('\))?", ex2.Message); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] public void NullEncryptionAlgorithm() { - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, null, cek)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, null, cek)); Assert.Matches($@"Internal error. Key encryption algorithm cannot be null.\s+\(?Parameter (name: )?'?encryptionAlgorithm('\))?", ex1.Message); - Exception ex2 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, null, cek)); + Exception ex2 = Assert.Throws(() => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(_fixture.AkvKeyUrl, null, cek)); Assert.Matches($@"Internal error. Key encryption algorithm cannot be null.\s+\(?Parameter (name: )?'?encryptionAlgorithm('\))?", ex2.Message); } @@ -53,28 +53,28 @@ public void NullEncryptionAlgorithm() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] public void EmptyColumnEncryptionKey() { - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, new byte[] { })); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(_fixture.AkvKeyUrl, MasterKeyEncAlgo, new byte[] { })); Assert.Matches($@"Internal error. Empty columnEncryptionKey specified.", ex1.Message); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] public void NullColumnEncryptionKey() { - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, null)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(_fixture.AkvKeyUrl, MasterKeyEncAlgo, null)); Assert.Matches($@"Value cannot be null.\s+\(?Parameter (name: )?'?columnEncryptionKey('\))?", ex1.Message); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] public void EmptyEncryptedColumnEncryptionKey() { - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, new byte[] { })); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, MasterKeyEncAlgo, new byte[] { })); Assert.Matches($@"Internal error. Empty encryptedColumnEncryptionKey specified", ex1.Message); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] public void NullEncryptedColumnEncryptionKey() { - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, null)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, MasterKeyEncAlgo, null)); Assert.Matches($@"Value cannot be null.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?", ex1.Message); } @@ -82,10 +82,10 @@ public void NullEncryptedColumnEncryptionKey() public void InvalidAlgorithmVersion() { byte[] encrypteCekLocal = ColumnEncryptionKey.GenerateInvalidEncryptedCek(encryptedCek, ColumnEncryptionKey.ECEKCorruption.ALGORITHM_VERSION); - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, encrypteCekLocal)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, MasterKeyEncAlgo, encrypteCekLocal)); Assert.Matches($@"Specified encrypted column encryption key contains an invalid encryption algorithm version '10'. Expected version is '01'.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?", ex1.Message); - Exception ex2 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, "RSA_CORRUPT", encryptedCek)); + Exception ex2 = Assert.Throws(() => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(_fixture.AkvKeyUrl, "RSA_CORRUPT", encryptedCek)); Assert.Contains("Invalid key encryption algorithm specified: 'RSA_CORRUPT'. Expected value: 'RSA_OAEP' or 'RSA-OAEP'.", ex2.Message); } @@ -95,9 +95,9 @@ public void InvalidCertificateSignature() // Put an invalid signature byte[] encrypteCekLocal = ColumnEncryptionKey.GenerateInvalidEncryptedCek(encryptedCek, ColumnEncryptionKey.ECEKCorruption.SIGNATURE); string errorMessage = - $@"The specified encrypted column encryption key signature does not match the signature computed with the column master key \(Asymmetric key in Azure Key Vault\) in '{DataTestUtility.AKVUrl}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?"; + $@"The specified encrypted column encryption key signature does not match the signature computed with the column master key \(Asymmetric key in Azure Key Vault\) in '{_fixture.AkvKeyUrl}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?"; - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, encrypteCekLocal)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, MasterKeyEncAlgo, encrypteCekLocal)); Assert.Matches(errorMessage, ex1.Message); } @@ -106,9 +106,9 @@ public void InvalidCipherTextLength() { // Put an invalid signature byte[] encrypteCekLocal = ColumnEncryptionKey.GenerateInvalidEncryptedCek(encryptedCek, ColumnEncryptionKey.ECEKCorruption.CEK_LENGTH); - string errorMessage = $@"The specified encrypted column encryption key's ciphertext length: 251 does not match the ciphertext length: 256 when using column master key \(Azure Key Vault key\) in '{DataTestUtility.AKVUrl}'. The encrypted column encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?"; + string errorMessage = $@"The specified encrypted column encryption key's ciphertext length: 251 does not match the ciphertext length: 256 when using column master key \(Azure Key Vault key\) in '{_fixture.AkvKeyUrl}'. The encrypted column encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?"; - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, encrypteCekLocal)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, MasterKeyEncAlgo, encrypteCekLocal)); Assert.Matches(errorMessage, ex1.Message); } @@ -116,9 +116,9 @@ public void InvalidCipherTextLength() public void InvalidSignatureInEncryptedCek() { byte[] encryptedCekLocal = ColumnEncryptionKey.GenerateInvalidEncryptedCek(encryptedCek, ColumnEncryptionKey.ECEKCorruption.SIGNATURE_LENGTH); - string errorMessage = $@"The specified encrypted column encryption key's signature length: 249 does not match the signature length: 256 when using column master key \(Azure Key Vault key\) in '{DataTestUtility.AKVUrl}'. The encrypted column encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?"; + string errorMessage = $@"The specified encrypted column encryption key's signature length: 249 does not match the signature length: 256 when using column master key \(Azure Key Vault key\) in '{_fixture.AkvKeyUrl}'. The encrypted column encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?"; - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, encryptedCekLocal)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(_fixture.AkvKeyUrl, MasterKeyEncAlgo, encryptedCekLocal)); Assert.Matches(errorMessage, ex1.Message); } @@ -134,10 +134,10 @@ public void InvalidURL() string fakePath = new string(barePath); string errorMessage = $@"Invalid url specified: '{fakePath}'.\s+\(?Parameter (name: )?'?masterKeyPath('\))?"; - Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(fakePath, MasterKeyEncAlgo, cek)); + Exception ex1 = Assert.Throws(() => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(fakePath, MasterKeyEncAlgo, cek)); Assert.Matches(errorMessage, ex1.Message); - Exception ex2 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(fakePath, MasterKeyEncAlgo, encryptedCek)); + Exception ex2 = Assert.Throws(() => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(fakePath, MasterKeyEncAlgo, encryptedCek)); Assert.Matches(errorMessage, ex2.Message); } @@ -145,11 +145,11 @@ public void InvalidURL() public void NullAKVKeyPath() { Exception ex1 = Assert.Throws( - () => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(null, MasterKeyEncAlgo, cek)); + () => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(null, MasterKeyEncAlgo, cek)); Assert.Matches($@"Azure Key Vault key path cannot be null.\s+\(?Parameter (name: )?'?masterKeyPath('\))?", ex1.Message); Exception ex2 = Assert.Throws( - () => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(null, MasterKeyEncAlgo, encryptedCek)); + () => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(null, MasterKeyEncAlgo, encryptedCek)); Assert.Matches($@"Internal error. Azure Key Vault key path cannot be null.\s+\(?Parameter (name: )?'?masterKeyPath('\))?", ex2.Message); } @@ -185,19 +185,19 @@ public void InvalidCertificatePath() string invalidTrustedEndpointErrorMessage = $@"Invalid Azure Key Vault key path specified: '{dummyPathWithInvalidKey}'. Valid trusted endpoints: vault.azure.net, vault.azure.cn, vault.usgovcloudapi.net, vault.microsoftazure.de, managedhsm.azure.net, managedhsm.azure.cn, managedhsm.usgovcloudapi.net, managedhsm.microsoftazure.de.\s+\(?Parameter (name: )?'?masterKeyPath('\))?"; Exception ex = Assert.Throws( - () => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPathWithOnlyHost, MasterKeyEncAlgo, cek)); + () => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPathWithOnlyHost, MasterKeyEncAlgo, cek)); Assert.Matches(invalidUrlErrorMessage, ex.Message); ex = Assert.Throws( - () => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPathWithInvalidKey, MasterKeyEncAlgo, cek)); + () => _fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPathWithInvalidKey, MasterKeyEncAlgo, cek)); Assert.Matches(invalidTrustedEndpointErrorMessage, ex.Message); ex = Assert.Throws( - () => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPathWithOnlyHost, MasterKeyEncAlgo, encryptedCek)); + () => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPathWithOnlyHost, MasterKeyEncAlgo, encryptedCek)); Assert.Matches(invalidUrlErrorMessage, ex.Message); ex = Assert.Throws( - () => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPathWithInvalidKey, MasterKeyEncAlgo, encryptedCek)); + () => _fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPathWithInvalidKey, MasterKeyEncAlgo, encryptedCek)); Assert.Matches(invalidTrustedEndpointErrorMessage, ex.Message); } @@ -207,11 +207,11 @@ public void InvalidCertificatePath() public void AkvStoreProviderVerifyFunctionWithInvalidSignature(bool fEnclaveEnabled) { //sign the cmk - byte[] cmkSignature = fixture.AkvStoreProvider.SignColumnMasterKeyMetadata(DataTestUtility.AKVUrl, allowEnclaveComputations: fEnclaveEnabled); + byte[] cmkSignature = _fixture.AkvStoreProvider.SignColumnMasterKeyMetadata(_fixture.AkvKeyUrl, allowEnclaveComputations: fEnclaveEnabled); Assert.True(cmkSignature != null); // Expect failed verification for a toggle of enclaveEnabled bit - Assert.False(fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, allowEnclaveComputations: !fEnclaveEnabled, signature: cmkSignature)); + Assert.False(_fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(_fixture.AkvKeyUrl, allowEnclaveComputations: !fEnclaveEnabled, signature: cmkSignature)); // Prepare another cipherText buffer byte[] tamperedCmkSignature = new byte[cmkSignature.Length]; @@ -223,7 +223,7 @@ public void AkvStoreProviderVerifyFunctionWithInvalidSignature(bool fEnclaveEnab byte[] randomIndexInCipherText = new byte[1]; for (int i = 0; i < 10; i++) { - Assert.True(fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, allowEnclaveComputations: fEnclaveEnabled, signature: tamperedCmkSignature), @"tamperedCmkSignature before tampering should be verified without any problems."); + Assert.True(_fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(_fixture.AkvKeyUrl, allowEnclaveComputations: fEnclaveEnabled, signature: tamperedCmkSignature), @"tamperedCmkSignature before tampering should be verified without any problems."); int startingByteIndex = 0; rng.GetBytes(randomIndexInCipherText); @@ -231,7 +231,7 @@ public void AkvStoreProviderVerifyFunctionWithInvalidSignature(bool fEnclaveEnab tamperedCmkSignature[startingByteIndex + randomIndexInCipherText[0]] = (byte)(cmkSignature[startingByteIndex + randomIndexInCipherText[0]] + 1); // Expect failed verification for invalid signature bytes - Assert.False(fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, allowEnclaveComputations: fEnclaveEnabled, signature: tamperedCmkSignature)); + Assert.False(_fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(_fixture.AkvKeyUrl, allowEnclaveComputations: fEnclaveEnabled, signature: tamperedCmkSignature)); // Fix up the corrupted byte tamperedCmkSignature[startingByteIndex + randomIndexInCipherText[0]] = cmkSignature[startingByteIndex + randomIndexInCipherText[0]]; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/AzureKeyVaultKeyFixture.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/AzureKeyVaultKeyFixture.cs new file mode 100644 index 0000000000..4fea191d01 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/AzureKeyVaultKeyFixture.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.using System; + +using Microsoft.Data.SqlClient.TestUtilities.Fixtures; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted +{ + public sealed class AzureKeyVaultKeyFixture : AzureKeyVaultKeyFixtureBase + { + public AzureKeyVaultKeyFixture() + : base(DataTestUtility.AKVBaseUri, DataTestUtility.GetTokenCredential()) + { + GeneratedKeyUri = CreateKey(nameof(GeneratedKeyUri), 2048).ToString(); + } + + public string GeneratedKeyUri { get; } + } +} 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 4d3c635684..a3ef1e12b0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -9,15 +9,15 @@ using System.Security.Cryptography.X509Certificates; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.TestFixtures.Setup; +using Microsoft.Data.SqlClient.TestUtilities.Fixtures; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { - public class SQLSetupStrategy : IDisposable + public class SQLSetupStrategy : ColumnMasterKeyCertificateFixture { internal const string ColumnEncryptionAlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA256"; - protected static X509Certificate2 certificate; - public string keyPath { get; internal set; } + public string ColumnMasterKeyPath { get; } public Table ApiTestTable { get; private set; } public Table BulkCopyAEErrorMessageTestTable { get; private set; } public Table BulkCopyAETestTable { get; private set; } @@ -58,15 +58,16 @@ public class SQLSetupStrategy : IDisposable public Dictionary sqlBulkTruncationTableNames = new Dictionary(); public SQLSetupStrategy() + : base(true) { - if (certificate == null) - { - certificate = CertificateUtility.CreateCertificate(); - } - keyPath = string.Concat(StoreLocation.CurrentUser.ToString(), "/", StoreName.My.ToString(), "/", certificate.Thumbprint); + ColumnMasterKeyPath = $"{StoreLocation.CurrentUser}/{StoreName.My}/{ColumnMasterKeyCertificate.Thumbprint}"; } - protected SQLSetupStrategy(string customKeyPath) => keyPath = customKeyPath; + protected SQLSetupStrategy(string customKeyPath) + : base(false) + { + ColumnMasterKeyPath = customKeyPath; + } internal virtual void SetupDatabase() { @@ -87,7 +88,15 @@ internal virtual void SetupDatabase() } } - // Insert data for TrustedMasterKeyPaths tests. + } + // Insert data for TrustedMasterKeyPaths tests. + InsertSampleData(TrustedMasterKeyPathsTestTable.Name); + } + + protected void InsertSampleData(string tableName) + { + foreach(string value in DataTestUtility.AEConnStringsSetup) + { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(value) { ConnectTimeout = 10000 @@ -96,7 +105,9 @@ internal virtual void SetupDatabase() using (SqlConnection sqlConn = new SqlConnection(builder.ToString())) { sqlConn.Open(); - DatabaseHelper.InsertCustomerData(sqlConn, null, TrustedMasterKeyPathsTestTable.Name, customer); + + Table.DeleteData(tableName, sqlConn); + DatabaseHelper.InsertCustomerData(sqlConn, null, tableName, customer); } } } @@ -142,9 +153,12 @@ protected List
CreateTables(IList columnEncryptionKe SqlNullValuesTable = new SqlNullValuesTable(GenerateUniqueName("SqlNullValuesTable"), columnEncryptionKeys[0]); tables.Add(SqlNullValuesTable); - // columnEncryptionKeys[2] is encrypted with DummyCMK. use this encrypted column to test custom key store providers - CustomKeyStoreProviderTestTable = new ApiTestTable(GenerateUniqueName("CustomKeyStoreProviderTestTable"), columnEncryptionKeys[2], columnEncryptionKeys[0], useDeterministicEncryption: true); - tables.Add(CustomKeyStoreProviderTestTable); + if (columnEncryptionKeys.Count > 2) + { + // columnEncryptionKeys[2] is encrypted with DummyCMK. use this encrypted column to test custom key store providers + CustomKeyStoreProviderTestTable = new ApiTestTable(GenerateUniqueName("CustomKeyStoreProviderTestTable"), columnEncryptionKeys[2], columnEncryptionKeys[0], useDeterministicEncryption: true); + tables.Add(CustomKeyStoreProviderTestTable); + } TabNVarCharMaxSource = new BulkCopyTruncationTables(GenerateUniqueName("TabNVarCharMaxSource"), columnEncryptionKeys[0]); tables.Add(TabNVarCharMaxSource); @@ -255,13 +269,7 @@ 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) + protected override void Dispose(bool disposing) { databaseObjects.Reverse(); foreach (string value in DataTestUtility.AEConnStringsSetup) @@ -272,6 +280,7 @@ protected virtual void Dispose(bool disposing) databaseObjects.ForEach(o => o.Drop(sqlConnection)); } } + base.Dispose(disposing); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyAzureKeyVault.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyAzureKeyVault.cs index 0c593ab8da..abc7c68d98 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyAzureKeyVault.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyAzureKeyVault.cs @@ -4,6 +4,10 @@ using System; using System.Collections.Generic; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Azure; +using Azure.Security.KeyVault.Keys; using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; @@ -13,17 +17,25 @@ public class SQLSetupStrategyAzureKeyVault : SQLSetupStrategy { internal static bool IsAKVProviderRegistered = false; + private readonly List _akvKeyNames; + private readonly KeyClient _keyClient; + public Table AKVTestTable { get; private set; } public SqlColumnEncryptionAzureKeyVaultProvider AkvStoreProvider; public DummyMasterKeyForAKVProvider DummyMasterKey; + public string AkvKeyUrl { get; private set; } public SQLSetupStrategyAzureKeyVault() : base() { + _akvKeyNames = new List(); + _keyClient = new KeyClient(DataTestUtility.AKVBaseUri, DataTestUtility.GetTokenCredential()); AkvStoreProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential()); + if (!IsAKVProviderRegistered) { RegisterGlobalProviders(AkvStoreProvider); } + SetupAzureKeyVault(); SetupDatabase(); } @@ -42,10 +54,42 @@ public static void RegisterGlobalProviders(SqlColumnEncryptionAzureKeyVaultProvi IsAKVProviderRegistered = true; } + private static RSA CopyKey(RSA rsa) + { +#if NET + // In .NET Framework, the key is exportable in plaintext. + // We need to manually handle this in .NET 6.0 and 8.0 with a non-plaintext export. + RSA replacementKey = RSA.Create(rsa.KeySize); + Span passwordBytes = stackalloc byte[32]; + PbeParameters pbeParameters = new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 10000); + + Random.Shared.NextBytes(passwordBytes); + + replacementKey.ImportEncryptedPkcs8PrivateKey( + passwordBytes, + rsa.ExportEncryptedPkcs8PrivateKey(passwordBytes, pbeParameters), + out _); + return replacementKey; +#else + return rsa; +#endif + } + + private void SetupAzureKeyVault() + { + RSA rsaCopy = CopyKey(ColumnMasterKeyCertificate.GetRSAPrivateKey()); + JsonWebKey rsaImport = new JsonWebKey(rsaCopy, true); + string akvKeyName = $"AE-{ColumnMasterKeyCertificate.Thumbprint}"; + + _keyClient.ImportKey(akvKeyName, rsaImport); + _akvKeyNames.Add(akvKeyName); + AkvKeyUrl = (new Uri(DataTestUtility.AKVBaseUri, $"/keys/{akvKeyName}")).AbsoluteUri; + } + internal override void SetupDatabase() { - ColumnMasterKey akvColumnMasterKey = new AkvColumnMasterKey(GenerateUniqueName("AKVCMK"), akvUrl: DataTestUtility.AKVUrl, AkvStoreProvider, DataTestUtility.EnclaveEnabled); - DummyMasterKey = new DummyMasterKeyForAKVProvider(GenerateUniqueName("DummyCMK"), DataTestUtility.AKVUrl, AkvStoreProvider, false); + ColumnMasterKey akvColumnMasterKey = new AkvColumnMasterKey(GenerateUniqueName("AKVCMK"), akvUrl: AkvKeyUrl, AkvStoreProvider, DataTestUtility.EnclaveEnabled); + DummyMasterKey = new DummyMasterKeyForAKVProvider(GenerateUniqueName("DummyCMK"), AkvKeyUrl, AkvStoreProvider, false); databaseObjects.Add(akvColumnMasterKey); databaseObjects.Add(DummyMasterKey); @@ -62,5 +106,22 @@ internal override void SetupDatabase() base.SetupDatabase(); } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + foreach (string keyName in _akvKeyNames) + { + try + { + _keyClient.StartDeleteKey(keyName); + } + catch (Exception) + { + continue; + } + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCertStoreProvider.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCertStoreProvider.cs index ae71dfc446..db935a5fed 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCertStoreProvider.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCertStoreProvider.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information.using System; using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted @@ -20,12 +19,10 @@ public SQLSetupStrategyCertStoreProvider() : base() SetupDatabase(); } - protected SQLSetupStrategyCertStoreProvider(string customKeyPath) => keyPath = customKeyPath; - internal override void SetupDatabase() { - CspColumnMasterKey = new CspColumnMasterKey(GenerateUniqueName("CMK"), certificate.Thumbprint, CertStoreProvider, DataTestUtility.EnclaveEnabled); - DummyMasterKey = new DummyMasterKeyForCertStoreProvider(GenerateUniqueName("DummyCMK"), certificate.Thumbprint, CertStoreProvider, false); + CspColumnMasterKey = new CspColumnMasterKey(GenerateUniqueName("CMK"), ColumnMasterKeyCertificate.Thumbprint, CertStoreProvider, DataTestUtility.EnclaveEnabled); + DummyMasterKey = new DummyMasterKeyForCertStoreProvider(GenerateUniqueName("DummyCMK"), ColumnMasterKeyCertificate.Thumbprint, CertStoreProvider, false); databaseObjects.Add(CspColumnMasterKey); databaseObjects.Add(DummyMasterKey); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs deleted file mode 100644 index 3b965d5e7f..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs +++ /dev/null @@ -1,50 +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 Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; - -namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted -{ - public class SQLSetupStrategyCspExt : SQLSetupStrategyCertStoreProvider - { - public Table CspProviderTable { get; private set; } - public SqlColumnEncryptionCspProvider keyStoreProvider { get; } - - public SQLSetupStrategyCspExt(string cspKeyPath) : base(cspKeyPath) - { - keyStoreProvider = new SqlColumnEncryptionCspProvider(); - this.SetupDatabase(); - } - - internal override void SetupDatabase() - { - ColumnMasterKey columnMasterKey = new CspProviderColumnMasterKey(GenerateUniqueName("CspExt"), SqlColumnEncryptionCspProvider.ProviderName, keyPath); - databaseObjects.Add(columnMasterKey); - - List columnEncryptionKeys = CreateColumnEncryptionKeys(columnMasterKey, 2, keyStoreProvider); - databaseObjects.AddRange(columnEncryptionKeys); - - Table table = CreateTable(columnEncryptionKeys); - databaseObjects.Add(table); - - foreach(string value in DataTestUtility.AEConnStringsSetup) - { - using (SqlConnection sqlConnection = new SqlConnection(value)) - { - sqlConnection.Open(); - databaseObjects.ForEach(o => o.Create(sqlConnection)); - } - } - - } - - private Table CreateTable(IList columnEncryptionKeys) - { - CspProviderTable = new ApiTestTable(GenerateUniqueName("CspProviderTable"), columnEncryptionKeys[0], columnEncryptionKeys[1]); - - return CspProviderTable; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspProvider.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspProvider.cs new file mode 100644 index 0000000000..08cdfe48ec --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspProvider.cs @@ -0,0 +1,77 @@ +// 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; +using System.Collections.Generic; +using System.Security.Cryptography; +using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted +{ + public class SQLSetupStrategyCspProvider : SQLSetupStrategy + { + private const int KeySize = 2048; + + private readonly List _cspKeyParameters = new List(); + + public SQLSetupStrategyCspProvider(CspParameters cspParameters) + : base(cspParameters.ProviderName + "/" + cspParameters.KeyContainerName) + { + // Create a new instance of RSACryptoServiceProvider to generate + // a new key pair. Pass the CspParameters class to persist the + // key in the container. + using RSACryptoServiceProvider rsaAlg = new RSACryptoServiceProvider(KeySize, cspParameters); + rsaAlg.PersistKeyInCsp = true; + + _cspKeyParameters.Add(cspParameters); + + CspProvider = new SqlColumnEncryptionCspProvider(); + SetupDatabase(); + } + + public SqlColumnEncryptionCspProvider CspProvider { get; } + + internal override void SetupDatabase() + { + ColumnMasterKey columnMasterKey = new CspProviderColumnMasterKey(GenerateUniqueName("CspExt"), SqlColumnEncryptionCspProvider.ProviderName, ColumnMasterKeyPath); + databaseObjects.Add(columnMasterKey); + + List columnEncryptionKeys = CreateColumnEncryptionKeys(columnMasterKey, 2, CspProvider); + databaseObjects.AddRange(columnEncryptionKeys); + + List
tables = CreateTables(columnEncryptionKeys); + databaseObjects.AddRange(tables); + + base.SetupDatabase(); + + InsertSampleData(ApiTestTable.Name); + } + + protected override void Dispose(bool disposing) + { + foreach (CspParameters cspParameters in _cspKeyParameters) + { + try + { + // Create a new instance of RSACryptoServiceProvider. + // Pass the CspParameters class to use the + // key in the container. + using RSACryptoServiceProvider rsaAlg = new RSACryptoServiceProvider(cspParameters); + + // Delete the key entry in the container. + rsaAlg.PersistKeyInCsp = false; + + // Call Clear to release resources and delete the key from the container. + rsaAlg.Clear(); + } + catch (Exception) + { + continue; + } + } + + base.Dispose(disposing); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs index 3d3c717b31..dfe94050f7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs @@ -105,95 +105,6 @@ internal static void CleanSqlClientCache() } } - /// - /// Create a self-signed certificate. - /// - internal static X509Certificate2 CreateCertificate() - { - byte[] certificateRawBytes = new byte[] { 48, 130, 10, 36, 2, 1, 3, 48, 130, 9, 224, 6, 9, 42, 134, 72, 134, 247, 13, 1, 7, 1, 160, 130, 9, 209, 4, 130, 9, 205, 48, 130, 9, 201, 48, 130, 5, 250, 6, 9, 42, 134, 72, 134, 247, 13, 1, 7, 1, 160, 130, 5, 235, 4, 130, 5, 231, 48, 130, 5, 227, 48, 130, 5, 223, 6, 11, 42, 134, 72, 134, 247, 13, 1, 12, 10, 1, 2, 160, 130, 4, 254, 48, 130, 4, 250, 48, 28, 6, 10, 42, 134, 72, 134, 247, 13, 1, 12, 1, 3, 48, 14, 4, 8, 146, 126, 191, 6, 130, 18, 111, 71, 2, 2, 7, 208, 4, 130, 4, 216, 55, 138, 10, 135, 82, 84, 240, 82, 107, 75, 21, 156, 54, 53, 188, 62, 36, 248, 59, 17, 18, 41, 206, 171, 226, 168, 175, 59, 48, 50, 36, 26, 58, 39, 118, 231, 200, 107, 86, 144, 200, 20, 135, 22, 105, 159, 229, 116, 123, 122, 194, 69, 172, 171, 128, 251, 129, 222, 113, 27, 253, 48, 164, 116, 72, 194, 123, 12, 247, 186, 162, 40, 39, 114, 22, 118, 91, 192, 73, 122, 235, 247, 40, 89, 3, 222, 64, 214, 184, 67, 204, 188, 197, 188, 107, 126, 225, 194, 161, 110, 156, 45, 70, 26, 86, 69, 63, 120, 153, 164, 136, 15, 220, 153, 104, 50, 121, 87, 10, 180, 149, 98, 220, 73, 175, 50, 146, 231, 112, 230, 204, 132, 76, 43, 142, 7, 104, 142, 146, 92, 21, 52, 38, 59, 154, 108, 159, 192, 93, 174, 39, 134, 96, 189, 150, 77, 90, 160, 43, 127, 173, 199, 189, 4, 69, 44, 104, 148, 225, 44, 149, 167, 149, 121, 220, 232, 98, 131, 212, 130, 35, 79, 10, 173, 177, 150, 161, 91, 26, 12, 221, 136, 230, 124, 73, 96, 126, 12, 241, 99, 60, 140, 126, 140, 0, 166, 47, 16, 87, 102, 138, 45, 97, 21, 31, 224, 126, 231, 102, 99, 35, 207, 75, 22, 249, 115, 51, 106, 79, 208, 21, 108, 124, 143, 108, 130, 6, 61, 215, 227, 7, 224, 174, 193, 97, 211, 241, 224, 90, 37, 101, 147, 149, 173, 239, 113, 214, 1, 41, 69, 158, 203, 3, 63, 101, 196, 134, 7, 127, 58, 113, 243, 228, 162, 99, 75, 207, 153, 19, 193, 187, 52, 124, 85, 234, 7, 249, 75, 65, 230, 107, 247, 145, 64, 94, 106, 50, 117, 83, 138, 49, 10, 22, 211, 115, 183, 20, 119, 18, 117, 166, 153, 30, 210, 248, 118, 200, 21, 180, 118, 208, 53, 90, 243, 74, 76, 109, 106, 46, 103, 112, 197, 89, 92, 178, 83, 48, 97, 162, 73, 78, 105, 145, 213, 230, 17, 211, 121, 200, 101, 179, 158, 85, 99, 211, 68, 122, 234, 176, 4, 33, 225, 120, 139, 163, 110, 35, 199, 23, 45, 126, 199, 80, 145, 14, 74, 217, 200, 172, 216, 159, 237, 241, 157, 85, 210, 141, 180, 150, 187, 82, 48, 245, 154, 125, 60, 223, 244, 21, 20, 39, 88, 8, 153, 185, 227, 76, 78, 137, 99, 98, 81, 141, 27, 197, 41, 39, 251, 80, 27, 85, 78, 65, 15, 216, 106, 106, 113, 33, 253, 210, 46, 214, 47, 49, 89, 170, 215, 207, 62, 182, 88, 25, 186, 166, 214, 172, 63, 94, 17, 123, 235, 226, 72, 73, 204, 18, 173, 134, 92, 66, 2, 213, 151, 251, 95, 175, 38, 56, 156, 138, 96, 123, 190, 107, 59, 230, 24, 210, 224, 206, 169, 159, 95, 180, 237, 34, 194, 62, 4, 213, 228, 85, 216, 138, 157, 50, 20, 101, 160, 195, 138, 207, 18, 17, 232, 6, 73, 82, 247, 173, 50, 180, 53, 58, 156, 97, 230, 112, 211, 251, 204, 120, 188, 34, 41, 67, 83, 197, 131, 251, 176, 20, 70, 169, 116, 237, 43, 117, 45, 31, 66, 74, 152, 216, 3, 108, 102, 99, 5, 127, 76, 129, 57, 180, 90, 218, 157, 108, 85, 4, 240, 101, 149, 154, 221, 208, 70, 152, 34, 128, 57, 135, 38, 17, 139, 142, 167, 109, 73, 129, 181, 105, 45, 151, 106, 171, 166, 0, 113, 147, 141, 19, 228, 196, 88, 175, 219, 18, 213, 54, 105, 179, 8, 249, 250, 164, 86, 28, 185, 19, 60, 50, 140, 73, 237, 148, 201, 33, 204, 189, 43, 83, 163, 138, 1, 10, 13, 240, 196, 211, 221, 169, 207, 100, 167, 203, 146, 115, 70, 118, 230, 4, 224, 192, 209, 242, 144, 150, 72, 170, 149, 255, 196, 7, 91, 55, 251, 57, 127, 103, 98, 113, 83, 224, 97, 118, 132, 81, 119, 8, 105, 250, 155, 107, 149, 28, 127, 66, 127, 224, 79, 96, 9, 168, 73, 84, 228, 123, 161, 222, 179, 115, 73, 184, 62, 24, 228, 44, 156, 42, 124, 209, 29, 81, 19, 169, 24, 212, 6, 238, 239, 221, 68, 220, 106, 0, 45, 201, 129, 3, 50, 150, 244, 32, 220, 237, 20, 39, 175, 249, 80, 189, 166, 68, 251, 102, 60, 137, 93, 209, 86, 194, 55, 164, 100, 76, 220, 249, 30, 233, 101, 177, 150, 71, 28, 227, 180, 44, 115, 83, 201, 129, 44, 128, 247, 68, 175, 97, 36, 170, 76, 236, 57, 119, 240, 0, 129, 185, 35, 160, 231, 183, 56, 162, 197, 237, 186, 109, 118, 232, 84, 108, 125, 93, 92, 101, 193, 180, 210, 192, 244, 47, 55, 56, 217, 178, 200, 168, 232, 80, 223, 209, 255, 234, 146, 46, 215, 170, 197, 94, 84, 213, 233, 140, 247, 69, 185, 103, 183, 91, 23, 232, 32, 246, 244, 30, 41, 156, 28, 72, 109, 90, 127, 135, 132, 19, 136, 233, 168, 29, 98, 17, 111, 5, 185, 234, 86, 234, 114, 47, 227, 81, 77, 108, 179, 184, 91, 31, 74, 23, 29, 248, 41, 207, 8, 23, 181, 33, 99, 217, 48, 145, 97, 126, 139, 133, 11, 100, 69, 151, 146, 38, 79, 231, 155, 92, 134, 139, 189, 237, 132, 196, 95, 45, 141, 15, 26, 37, 58, 219, 10, 0, 36, 221, 240, 82, 117, 163, 121, 141, 206, 21, 180, 195, 58, 109, 56, 123, 152, 206, 116, 161, 221, 125, 248, 23, 31, 240, 227, 186, 52, 171, 147, 51, 39, 203, 92, 205, 182, 146, 149, 111, 27, 59, 219, 234, 216, 52, 89, 22, 224, 76, 62, 94, 76, 131, 48, 162, 134, 161, 177, 44, 205, 101, 253, 13, 237, 40, 29, 72, 224, 121, 74, 189, 57, 81, 58, 169, 178, 173, 157, 182, 143, 205, 64, 225, 137, 188, 235, 43, 195, 3, 187, 105, 113, 72, 82, 153, 58, 97, 38, 251, 212, 149, 191, 11, 153, 157, 106, 16, 236, 237, 209, 210, 208, 19, 68, 92, 176, 65, 24, 115, 181, 94, 24, 126, 2, 216, 63, 200, 136, 178, 92, 248, 11, 128, 68, 122, 14, 46, 234, 48, 142, 219, 92, 29, 136, 70, 200, 52, 78, 70, 160, 215, 113, 102, 190, 66, 16, 69, 120, 25, 201, 23, 209, 41, 79, 25, 151, 38, 38, 82, 244, 143, 121, 216, 111, 91, 167, 232, 32, 234, 243, 195, 168, 240, 135, 188, 1, 92, 145, 77, 240, 107, 20, 82, 147, 168, 132, 78, 115, 206, 95, 47, 8, 80, 91, 255, 28, 38, 161, 52, 168, 211, 236, 143, 238, 146, 172, 104, 2, 254, 240, 229, 210, 225, 47, 41, 76, 134, 5, 20, 203, 188, 48, 195, 120, 103, 234, 94, 217, 142, 238, 254, 131, 146, 214, 106, 212, 229, 201, 79, 151, 198, 100, 132, 99, 228, 82, 182, 94, 216, 226, 163, 42, 113, 110, 201, 70, 221, 127, 242, 7, 176, 60, 121, 158, 37, 56, 6, 156, 191, 75, 94, 222, 10, 155, 39, 64, 172, 216, 106, 210, 202, 246, 66, 83, 107, 250, 17, 134, 222, 212, 71, 200, 215, 103, 35, 82, 225, 106, 17, 106, 74, 18, 130, 236, 175, 45, 145, 155, 169, 88, 72, 244, 3, 38, 245, 208, 49, 129, 205, 48, 19, 6, 9, 42, 134, 72, 134, 247, 13, 1, 9, 21, 49, 6, 4, 4, 1, 0, 0, 0, 48, 87, 6, 9, 42, 134, 72, 134, 247, 13, 1, 9, 20, 49, 74, 30, 72, 0, 100, 0, 99, 0, 99, 0, 52, 0, 51, 0, 48, 0, 56, 0, 56, 0, 45, 0, 50, 0, 57, 0, 54, 0, 53, 0, 45, 0, 52, 0, 57, 0, 97, 0, 48, 0, 45, 0, 56, 0, 51, 0, 54, 0, 53, 0, 45, 0, 50, 0, 52, 0, 101, 0, 52, 0, 97, 0, 52, 0, 49, 0, 100, 0, 55, 0, 50, 0, 52, 0, 48, 48, 93, 6, 9, 43, 6, 1, 4, 1, 130, 55, 17, 1, 49, 80, 30, 78, 0, 77, 0, 105, 0, 99, 0, 114, 0, 111, 0, 115, 0, 111, 0, 102, 0, 116, 0, 32, 0, 83, 0, 116, 0, 114, 0, 111, 0, 110, 0, 103, 0, 32, 0, 67, 0, 114, 0, 121, 0, 112, 0, 116, 0, 111, 0, 103, 0, 114, 0, 97, 0, 112, 0, 104, 0, 105, 0, 99, 0, 32, 0, 80, 0, 114, 0, 111, 0, 118, 0, 105, 0, 100, 0, 101, 0, 114, 48, 130, 3, 199, 6, 9, 42, 134, 72, 134, 247, 13, 1, 7, 6, 160, 130, 3, 184, 48, 130, 3, 180, 2, 1, 0, 48, 130, 3, 173, 6, 9, 42, 134, 72, 134, 247, 13, 1, 7, 1, 48, 28, 6, 10, 42, 134, 72, 134, 247, 13, 1, 12, 1, 3, 48, 14, 4, 8, 206, 244, 28, 93, 203, 68, 165, 233, 2, 2, 7, 208, 128, 130, 3, 128, 74, 136, 80, 43, 195, 182, 181, 122, 132, 229, 10, 181, 229, 1, 78, 122, 145, 95, 16, 236, 242, 107, 9, 141, 186, 205, 32, 139, 154, 132, 184, 180, 80, 26, 3, 85, 196, 10, 33, 216, 101, 105, 172, 196, 77, 222, 232, 229, 37, 199, 6, 189, 152, 8, 203, 15, 231, 164, 140, 163, 120, 23, 137, 34, 16, 241, 186, 64, 11, 241, 210, 160, 186, 90, 55, 39, 21, 210, 145, 74, 151, 40, 122, 221, 240, 191, 185, 115, 85, 208, 125, 136, 51, 210, 137, 124, 155, 65, 135, 50, 35, 233, 223, 157, 131, 108, 11, 142, 152, 217, 162, 163, 218, 47, 89, 255, 229, 21, 224, 139, 187, 4, 175, 251, 248, 8, 18, 16, 112, 134, 75, 17, 90, 246, 62, 150, 31, 207, 95, 172, 5, 220, 135, 201, 179, 247, 193, 177, 23, 5, 170, 207, 66, 219, 145, 117, 99, 167, 238, 100, 158, 169, 44, 22, 199, 132, 38, 67, 203, 66, 187, 53, 216, 98, 113, 76, 142, 153, 36, 238, 110, 152, 251, 68, 6, 154, 255, 51, 65, 75, 91, 9, 121, 86, 116, 35, 224, 47, 220, 194, 17, 136, 175, 76, 165, 210, 153, 89, 104, 197, 133, 200, 49, 173, 1, 167, 5, 88, 183, 58, 193, 146, 30, 60, 129, 195, 3, 16, 78, 87, 167, 135, 182, 182, 150, 68, 116, 161, 116, 125, 180, 155, 103, 63, 0, 98, 27, 179, 142, 64, 73, 31, 35, 63, 138, 137, 30, 169, 149, 221, 104, 21, 182, 23, 67, 246, 2, 162, 217, 165, 238, 124, 229, 149, 84, 5, 203, 174, 149, 79, 153, 25, 153, 233, 213, 86, 250, 10, 42, 6, 226, 113, 123, 90, 76, 153, 39, 203, 237, 124, 36, 191, 232, 132, 127, 82, 163, 109, 100, 121, 54, 254, 116, 155, 26, 255, 50, 150, 140, 172, 240, 208, 245, 65, 72, 49, 183, 149, 220, 244, 120, 193, 37, 222, 144, 137, 82, 168, 233, 13, 179, 2, 217, 29, 177, 4, 136, 69, 192, 133, 249, 180, 9, 62, 162, 216, 251, 164, 188, 173, 143, 149, 32, 204, 255, 246, 249, 33, 216, 75, 23, 127, 215, 134, 69, 79, 112, 213, 198, 89, 44, 51, 19, 226, 16, 210, 125, 212, 232, 18, 252, 178, 93, 245, 33, 62, 81, 207, 78, 167, 144, 238, 251, 27, 194, 21, 53, 44, 63, 58, 26, 176, 75, 79, 164, 67, 59, 80, 17, 54, 209, 58, 184, 2, 36, 202, 135, 91, 35, 78, 55, 203, 134, 238, 79, 178, 84, 242, 46, 223, 131, 227, 87, 255, 182, 244, 117, 162, 60, 134, 161, 49, 59, 95, 64, 190, 30, 195, 100, 106, 7, 120, 181, 202, 122, 174, 234, 30, 11, 88, 65, 238, 53, 64, 243, 233, 185, 168, 34, 8, 58, 233, 171, 210, 104, 105, 93, 49, 206, 11, 40, 172, 248, 204, 80, 128, 53, 143, 54, 95, 92, 70, 152, 209, 193, 116, 252, 138, 19, 50, 249, 43, 14, 225, 167, 8, 205, 112, 103, 79, 223, 14, 141, 147, 70, 197, 91, 11, 117, 202, 19, 180, 240, 21, 118, 108, 25, 63, 54, 94, 156, 112, 109, 16, 216, 113, 192, 246, 207, 156, 203, 65, 75, 143, 157, 125, 158, 151, 167, 207, 96, 6, 162, 97, 66, 114, 95, 227, 52, 44, 98, 121, 139, 181, 240, 89, 27, 59, 156, 189, 93, 28, 48, 165, 11, 245, 102, 198, 29, 5, 6, 180, 147, 58, 130, 65, 201, 10, 164, 193, 93, 168, 96, 156, 89, 225, 139, 70, 245, 74, 128, 3, 141, 133, 137, 21, 163, 77, 3, 19, 226, 35, 248, 156, 56, 56, 37, 221, 69, 67, 214, 3, 152, 149, 224, 92, 72, 173, 39, 196, 229, 153, 67, 151, 190, 115, 20, 70, 126, 210, 140, 109, 186, 46, 82, 88, 185, 96, 1, 254, 161, 217, 130, 226, 133, 18, 103, 175, 132, 249, 102, 51, 229, 192, 94, 44, 10, 25, 197, 237, 77, 196, 1, 253, 153, 78, 237, 151, 136, 89, 203, 113, 244, 217, 235, 252, 31, 116, 139, 233, 40, 197, 22, 176, 157, 130, 109, 149, 215, 11, 20, 3, 156, 239, 29, 250, 95, 188, 241, 184, 117, 108, 216, 74, 91, 169, 186, 122, 175, 214, 36, 62, 240, 142, 107, 172, 7, 250, 31, 101, 75, 83, 255, 56, 8, 231, 200, 194, 154, 105, 202, 170, 207, 252, 128, 10, 249, 53, 41, 168, 94, 225, 163, 10, 251, 149, 64, 10, 144, 252, 44, 136, 149, 119, 183, 7, 230, 87, 160, 46, 62, 185, 82, 218, 213, 125, 62, 70, 43, 27, 5, 181, 50, 193, 11, 30, 0, 8, 81, 94, 169, 171, 143, 113, 235, 171, 38, 129, 116, 11, 191, 75, 235, 185, 184, 178, 36, 193, 174, 177, 51, 87, 163, 142, 52, 62, 161, 237, 139, 50, 51, 227, 188, 164, 106, 233, 209, 8, 237, 241, 92, 145, 51, 6, 36, 197, 24, 255, 143, 5, 144, 43, 87, 242, 208, 251, 79, 171, 90, 103, 219, 73, 242, 95, 36, 48, 95, 127, 40, 128, 201, 80, 79, 74, 226, 25, 43, 50, 56, 180, 59, 84, 148, 110, 151, 9, 45, 4, 212, 172, 31, 189, 44, 115, 59, 169, 48, 59, 48, 31, 48, 7, 6, 5, 43, 14, 3, 2, 26, 4, 20, 238, 91, 24, 104, 64, 45, 237, 63, 114, 36, 111, 106, 82, 43, 251, 110, 60, 159, 42, 178, 4, 20, 20, 49, 70, 55, 115, 247, 221, 156, 47, 189, 197, 19, 116, 77, 161, 163, 216, 77, 166, 144, 2, 2, 7, 208 }; - X509Certificate2 certificate = new X509Certificate2(certificateRawBytes, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.Exportable); - X509Store certStore = null; - try - { - certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); - certStore.Open(OpenFlags.ReadWrite); - if (!certStore.Certificates.Contains(certificate)) - { - certStore.Add(certificate); - } - - } - finally - { - if (certStore != null) - { - certStore.Close(); - } - } - - if (DataTestUtility.IsAKVSetupAvailable()) - { - SetupAKVKeysAsync().Wait(); - } - - return certificate; - } - - private static async Task SetupAKVKeysAsync() - { - KeyClient keyClient = new KeyClient(DataTestUtility.AKVBaseUri, DataTestUtility.GetTokenCredential()); - AsyncPageable keys = keyClient.GetPropertiesOfKeysAsync(); - IAsyncEnumerator enumerator = keys.GetAsyncEnumerator(); - - bool testAKVKeyExists = false; - try - { - while (await enumerator.MoveNextAsync()) - { - KeyProperties keyProperties = enumerator.Current; - if (keyProperties.Name.Equals(DataTestUtility.AKVKeyName)) - { - testAKVKeyExists = true; - } - } - } - finally - { - await enumerator.DisposeAsync(); - } - - if (!testAKVKeyExists) - { - var rsaKeyOptions = new CreateRsaKeyOptions(DataTestUtility.AKVKeyName, hardwareProtected: false) - { - KeySize = 2048, - ExpiresOn = DateTimeOffset.Now.AddYears(1) - }; - keyClient.CreateRsaKey(rsaKeyOptions); - } - } - - /// - /// Removes a certificate from the local certificate store (useful for test cleanup). - /// - internal static void RemoveCertificate(X509Certificate2 certificate) - { - X509Store certStore = null; - try - { - certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); - certStore.Open(OpenFlags.ReadWrite); - certStore.Remove(certificate); - } - finally - { - if (certStore != null) - { - certStore.Close(); - } - } - } - internal static byte[] DecryptRsaDirectly(byte[] rsaPfx, byte[] ciphertextCek, string masterKeyPath) { Debug.Assert(rsaPfx != null && rsaPfx.Length > 0); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtilityWin.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtilityWin.cs deleted file mode 100644 index 893362e027..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtilityWin.cs +++ /dev/null @@ -1,264 +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; -using System.Diagnostics; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using Xunit; -using System.Collections.Generic; -using static System.Net.Mime.MediaTypeNames; -#if NET6_0_OR_GREATER -using System.Runtime.Versioning; -#endif - -namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted -{ -#if NET6_0_OR_GREATER - [SupportedOSPlatform("windows")] -#endif - [PlatformSpecific(TestPlatforms.Windows)] - class CertificateUtilityWin - { - - public class CertificateOption - { - public string Subject { get; set; } - public string KeyExportPolicy { get; set; } = "Exportable"; - public string CertificateStoreLocation { get; set; } - public List> TextExtensions { get; set; } = new List> { new Tuple("2.5.29.3", "1.3.6.1.5.5.8.2.2", "1.3.6.1.4.1.311.10.3.11") }; - public string KeySpec { get; set; } = "KeyExchange"; - public string Provider { get; set; } = "Microsoft Enhanced RSA and AES Cryptographic Provider"; - public string Type { get; set; } = "24"; - public string KeyAlgorithm { get; set; } = "rsa"; - public string KeyLength { get; set; } = "2048"; - public string HashAlgorithm { get; set; } = "sha256"; - public string KeyUsage { get; set; } = "None"; - public string TestRoot { get; set; } = "-TestRoot"; - } - - private CertificateUtilityWin() - { - } - - /// - /// Create a self-signed certificate through PowerShell. - /// - internal static void CreateCertificate(CertificateOption option) - { - Assert.False(string.IsNullOrWhiteSpace(option.Subject), "FAILED: certificateName should not be null or empty."); - Assert.False(string.IsNullOrWhiteSpace(option.CertificateStoreLocation), "FAILED: certificateLocation should not be null or empty."); - - string powerShellPath = string.IsNullOrEmpty(DataTestUtility.PowerShellPath) ? "powershell.exe" : DataTestUtility.PowerShellPath; - ProcessStartInfo processStartInfo = new ProcessStartInfo(powerShellPath) - { - Arguments = $"New-SelfSignedCertificate -Subject " + - $"'CN={option.Subject}' " + - $"-KeyExportPolicy {option.KeyExportPolicy} " + - $"-CertStoreLocation '{option.CertificateStoreLocation}\\My' " + - $"-TextExtension @('{option.TextExtensions[0].Item1}={{text}}{option.TextExtensions[0].Item2},{option.TextExtensions[0].Item3}') " + - $"-KeySpec {option.KeySpec} " + - $"-Provider '{option.Provider}' " + - $"-Type {option.Type} " + - $"-KeyAlgorithm {option.KeyAlgorithm} " + - $"-KeyLength {option.KeyLength} " + - $"-HashAlgorithm {option.HashAlgorithm} " + - $"-KeyUsage {option.KeyUsage} " + - $"{option.TestRoot}", - Verb="runas" - }; - Process process = new() - { - StartInfo = processStartInfo - }; - process.StartInfo.UseShellExecute = true; - process.StartInfo.CreateNoWindow = true; - process.Start(); - process.WaitForExit(); - } - - /// - /// Creates an RSA 2048 key inside the specified CSP. - /// - /// CSP name - /// Container name - /// - internal static bool RSAPersistKeyInCsp(string providerName, string containerName) - { - try - { - const int KEYSIZE = 2048; - int providerType = GetProviderKey(providerName); - - // Create a new instance of CspParameters. - CspParameters cspParams = new CspParameters(providerType, providerName, containerName); - - //Create a new instance of RSACryptoServiceProvider to generate - //a new key pair. Pass the CspParameters class to persist the - //key in the container. - RSACryptoServiceProvider rsaAlg = new RSACryptoServiceProvider(KEYSIZE, cspParams); - rsaAlg.PersistKeyInCsp = true; - } - catch (CryptographicException e) - { - Console.WriteLine("\tFAILURE: The RSA key was not persisted in the container, \"{0}\".", containerName); - Console.WriteLine(@" {0}", e.Message); - return false; - } - - return true; - } - - /// - /// Deletes the specified RSA key - /// - /// CSP name - /// Container name to be deleted - /// - internal static bool RSADeleteKeyInCsp(string providerName, string containerName) - { - try - { - int providerType = GetProviderKey(providerName); - - // Create a new instance of CspParameters. - CspParameters cspParams = new CspParameters(providerType, providerName, containerName); - - //Create a new instance of RSACryptoServiceProvider. - //Pass the CspParameters class to use the - //key in the container. - RSACryptoServiceProvider rsaAlg = new RSACryptoServiceProvider(cspParams); - - //Delete the key entry in the container. - rsaAlg.PersistKeyInCsp = false; - - //Call Clear to release resources and delete the key from the container. - rsaAlg.Clear(); - } - catch (CryptographicException e) - { - Console.WriteLine("\tFAILURE: The RSA key was not deleted from the container, \"{0}\".", containerName); - Console.WriteLine("\t{0}", e.Message); - return false; - } - - return true; - } - - internal static int GetProviderKey(string providerName) - { - Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography\Defaults\Provider"); - Microsoft.Win32.RegistryKey providerKey = key.OpenSubKey(providerName); - return (int)providerKey.GetValue(@"Type"); - } - - /// - /// Checks if a cert exists or not - /// - /// - /// - /// - internal static bool CertificateExists(string certificateName, StoreLocation certificateStoreLocation) - { - Assert.False(string.IsNullOrWhiteSpace(certificateName), "FAILED: certificateName should not be null or empty."); - - X509Store certStore = null; - try - { - certStore = new X509Store(StoreName.My, certificateStoreLocation); - certStore.Open(OpenFlags.ReadOnly); - X509Certificate2Collection certCollection = certStore.Certificates.Find(X509FindType.FindBySubjectName, certificateName, validOnly: false); - - if (certCollection != null && certCollection.Count > 0) - { - return true; - } - else - { - return false; - } - } - finally - { - if (certStore != null) - { - certStore.Close(); - } - } - } - - /// - /// Gets the certificate. - /// - /// - /// - /// - internal static X509Certificate2 GetCertificate(string certificateName, StoreLocation certificateStoreLocation) - { - Assert.False(string.IsNullOrWhiteSpace(certificateName), "FAILED: certificateName should not be null or empty."); - X509Store certStore = null; - try - { - certStore = new X509Store(StoreName.My, certificateStoreLocation); - certStore.Open(OpenFlags.ReadOnly); - X509Certificate2Collection certCollection = certStore.Certificates.Find(X509FindType.FindBySubjectName, certificateName, validOnly: false); - Debug.Assert(certCollection != null && certCollection.Count > 0); - return certCollection[0]; - } - finally - { - if (certStore != null) - { - certStore.Close(); - } - } - } - - /// - /// Gets Csp path from a given certificate. - /// - internal static string GetCspPathFromCertificate(X509Certificate2 certificate) - { - if (certificate.GetRSAPrivateKey() is RSACryptoServiceProvider csp) - { - return string.Concat(csp.CspKeyContainerInfo.ProviderName, @"/", csp.CspKeyContainerInfo.KeyContainerName); - } - else - { - return null; - } - } - - /// - /// Removes a certificate from the store. Cleanup purposes. - /// - /// - /// - /// - internal static void RemoveCertificate(string certificateName, StoreLocation certificateStoreLocation) - { - Assert.False(string.IsNullOrWhiteSpace(certificateName), "FAILED: certificateName should not be null or empty."); - - X509Store certStore = null; - try - { - certStore = new X509Store(StoreName.My, certificateStoreLocation); - certStore.Open(OpenFlags.ReadWrite); - X509Certificate2Collection certificateCollection = certStore.Certificates.Find(X509FindType.FindBySubjectName, certificateName, validOnly: false); - - if (certificateCollection != null && certificateCollection.Count > 0) - { - certStore.RemoveRange(certificateCollection); - } - } - finally - { - if (certStore != null) - { - certStore.Close(); - } - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/Table.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/Table.cs index b18e5a0ae8..e19ff59f80 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/Table.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/Table.cs @@ -19,6 +19,9 @@ public override void Drop(SqlConnection sqlConnection) command.CommandText = sql; command.ExecuteNonQuery(); } + + // TODO: Remove. + System.Console.WriteLine($"Dropped table {Name} from {sqlConnection.ConnectionString}"); } public static void DeleteData(string tableName, SqlConnection sqlConnection) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs index c617a990b8..c88b2767ea 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs @@ -7,14 +7,14 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { public class TestTrustedMasterKeyPaths : IClassFixture { - private SQLSetupStrategyCertStoreProvider fixture; + private readonly string dummyThumbprint; private readonly string tableName; private readonly string columnMasterKeyPath; public TestTrustedMasterKeyPaths(SQLSetupStrategyCertStoreProvider fixture) { - columnMasterKeyPath = string.Format(@"{0}/{1}/{2}", StoreLocation.CurrentUser.ToString(), @"my", CertificateUtility.CreateCertificate().Thumbprint); - this.fixture = fixture; + dummyThumbprint = new string('F', fixture.ColumnMasterKeyCertificate.Thumbprint.Length); + columnMasterKeyPath = fixture.ColumnMasterKeyPath; tableName = fixture.TrustedMasterKeyPathsTestTable.Name; } @@ -147,18 +147,14 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithMultipleServers(string } // Add entries for one server - List server1TrustedKeyPaths = new List(); - - // Add some random key paths - foreach (char c in new char[] { 'A', 'B' }) + List server1TrustedKeyPaths = new List() { - string tempThumbprint = new string('F', CertificateUtility.CreateCertificate().Thumbprint.Length); - string invalidKeyPath = string.Format(@"{0}/my/{1}", StoreLocation.CurrentUser.ToString(), tempThumbprint); - server1TrustedKeyPaths.Add(invalidKeyPath); - } - - // Add the key path used by the test - server1TrustedKeyPaths.Add(columnMasterKeyPath); + // Add some random key paths + string.Format(@"{0}/my/{1}", StoreLocation.CurrentUser.ToString(), dummyThumbprint), + string.Format(@"{0}/my/{1}", StoreLocation.CurrentUser.ToString(), dummyThumbprint), + // Add the key path used by the test + columnMasterKeyPath + }; // Add it to the dictionary SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, server1TrustedKeyPaths); @@ -277,8 +273,7 @@ FROM [{tableName}] // Prepare dictionary with invalid key path List invalidKeyPathList = new List(); - string tempThumbprint = new string('F', CertificateUtility.CreateCertificate().Thumbprint.Length); - string invalidKeyPath = string.Format(@"{0}/my/{1}", StoreLocation.CurrentUser.ToString(), tempThumbprint); + string invalidKeyPath = string.Format(@"{0}/my/{1}", StoreLocation.CurrentUser.ToString(), dummyThumbprint); invalidKeyPathList.Add(invalidKeyPath); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, invalidKeyPathList); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 1375cbea2f..4f842cf142 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -37,8 +37,6 @@ public static class DataTestUtility public static readonly string AADPasswordConnectionString = null; public static readonly string AADServicePrincipalId = null; public static readonly string AADServicePrincipalSecret = null; - public static readonly string AKVBaseUrl = null; - public static readonly string AKVUrl = null; public static readonly string AKVOriginalUrl = null; public static readonly string AKVTenantId = null; public static readonly string LocalDbAppName = null; @@ -69,7 +67,6 @@ public static class DataTestUtility public static string AADUserIdentityAccessToken = null; public const string ApplicationClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38"; public const string UdtTestDbName = "UdtTestDb"; - public const string AKVKeyName = "TestSqlClientAzureKeyVaultProvider"; public const string EventSourcePrefix = "Microsoft.Data.SqlClient"; public const string MDSEventSourceName = "Microsoft.Data.SqlClient.EventSource"; public const string AKVEventSourceName = "Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.EventSource"; @@ -88,7 +85,7 @@ public static bool TcpConnectionStringDoesNotUseAadAuth { get { - SqlConnectionStringBuilder builder = new (TCPConnectionString); + SqlConnectionStringBuilder builder = new(TCPConnectionString); return builder.Authentication == SqlAuthenticationMethod.SqlPassword || builder.Authentication == SqlAuthenticationMethod.NotSpecified; } } @@ -143,8 +140,6 @@ static DataTestUtility() if (!string.IsNullOrEmpty(AKVOriginalUrl) && Uri.TryCreate(AKVOriginalUrl, UriKind.Absolute, out AKVBaseUri)) { AKVBaseUri = new Uri(AKVBaseUri, "/"); - AKVBaseUrl = AKVBaseUri.AbsoluteUri; - AKVUrl = (new Uri(AKVBaseUri, $"/keys/{AKVKeyName}")).AbsoluteUri; } AKVTenantId = c.AzureKeyVaultTenantId; @@ -339,7 +334,7 @@ public static bool IsNotAzureServer() // Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/17858869-support-always-encrypted-in-sql-data-warehouse public static bool IsAKVSetupAvailable() { - return !string.IsNullOrEmpty(AKVUrl) && !string.IsNullOrEmpty(UserManagedIdentityClientId) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse(); + return AKVBaseUri != null && !string.IsNullOrEmpty(UserManagedIdentityClientId) && !string.IsNullOrEmpty(AKVTenantId) && IsNotAzureSynapse(); } private static readonly DefaultAzureCredential s_defaultCredential = new(new DefaultAzureCredentialOptions { ManagedIdentityClientId = UserManagedIdentityClientId }); @@ -415,48 +410,176 @@ public static bool DoesHostAddressContainBothIPv4AndIPv6() } } + // Generate a new GUID and return the characters from its 1st and 4th + // parts, as shown here: + // + // 7ff01cb8-88c7-11f0-b433-00155d7e531e + // ^^^^^^^^ ^^^^ + // + // These 12 characters are concatenated together without any + // separators. These 2 parts typically comprise a timestamp and clock + // sequence, most likely to be unique for tests that generate names in + // quick succession. + private static string GetGuidParts() + { + var guid = Guid.NewGuid().ToString(); + // GOTCHA: The slice operator is inclusive of the start index and + // exclusive of the end index! + return guid.Substring(0, 8) + guid.Substring(19, 4); + } + /// - /// Generate a unique name to use in Sql Server; - /// some providers does not support names (Oracle supports up to 30). + /// Generate a short unique database object name, whose maximum length + /// is 30 characters, with the format: + /// + /// _ + /// + /// The Prefix will be truncated to satisfy the overall maximum length. + /// + /// The GUID parts will be the characters from the 1st and 4th blocks + /// from a traditional string representation, as shown here: + /// + /// 7ff01cb8-88c7-11f0-b433-00155d7e531e + /// ^^^^^^^^ ^^^^ + /// + /// These 2 parts typically comprise a timestamp and clock sequence, + /// most likely to be unique for tests that generate names in quick + /// succession. The 12 characters are concatenated together without any + /// separators. /// - /// The name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length). - /// Name without brackets. - /// Unique name by considering the Sql Server naming rules. - public static string GetUniqueName(string prefix, bool withBracket = true) - { - string escapeLeft = withBracket ? "[" : string.Empty; - string escapeRight = withBracket ? "]" : string.Empty; - string uniqueName = string.Format("{0}{1}_{2}_{3}{4}", - escapeLeft, - prefix, - DateTime.Now.Ticks.ToString("X", CultureInfo.InvariantCulture), // up to 8 characters - Guid.NewGuid().ToString().Substring(0, 6), // take the first 6 characters only - escapeRight); - return uniqueName; + /// + /// + /// The prefix to use when generating the unique name, truncated to at + /// most 18 characters when withBracket is false, and 16 characters when + /// withBracket is true. + /// + /// This should not contain any characters that cannot be used in + /// database object names. See: + /// + /// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers + /// + /// + /// + /// When true, the entire generated name will be enclosed in square + /// brackets, for example: + /// + /// [MyPrefix_7ff01cb811f0] + /// + /// + /// + /// A unique database object name, no more than 30 characters long. + /// + public static string GetShortName(string prefix, bool withBracket = true) + { + StringBuilder name = new(30); + + if (withBracket) + { + name.Append('['); + } + + int maxPrefixLength = withBracket ? 16 : 18; + if (prefix.Length > maxPrefixLength) + { + prefix = prefix.Substring(0, maxPrefixLength); + } + + name.Append(prefix); + name.Append('_'); + name.Append(GetGuidParts()); + + if (withBracket) + { + name.Append(']'); + } + + return name.ToString(); } /// - /// Uses environment values `UserName` and `MachineName` in addition to the specified `prefix` and current date - /// to generate a unique name to use in Sql Server; - /// SQL Server supports long names (up to 128 characters), add extra info for troubleshooting. + /// Generate a long unique database object name, whose maximum length is + /// 96 characters, with the format: + /// + /// ___ + /// + /// The Prefix will be truncated to satisfy the overall maximum length. + /// + /// The GUID Parts will be the characters from the 1st and 4th blocks + /// from a traditional string representation, as shown here: + /// + /// 7ff01cb8-88c7-11f0-b433-00155d7e531e + /// ^^^^^^^^ ^^^^ + /// + /// These 2 parts typically comprise a timestamp and clock sequence, + /// most likely to be unique for tests that generate names in quick + /// succession. The 12 characters are concatenated together without any + /// separators. + /// + /// The UserName and MachineName are obtained from the Environment, + /// and will be truncated to satisfy the maximum overall length. /// - /// Add the prefix to the generate string. - /// Database name must be pass with brackets by default. - /// Unique name by considering the Sql Server naming rules. - public static string GetUniqueNameForSqlServer(string prefix, bool withBracket = true) - { - string extendedPrefix = string.Format( - "{0}_{1}_{2}@{3}", - prefix, - Environment.UserName, - Environment.MachineName, - DateTime.Now.ToString("yyyy_MM_dd", CultureInfo.InvariantCulture)); - string name = GetUniqueName(extendedPrefix, withBracket); - if (name.Length > 128) - { - throw new ArgumentOutOfRangeException("the name is too long - SQL Server names are limited to 128"); - } - return name; + /// + /// + /// The prefix to use when generating the unique name, truncated to at + /// most 32 characters. + /// + /// This should not contain any characters that cannot be used in + /// database object names. See: + /// + /// https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-ver17#rules-for-regular-identifiers + /// + /// + /// + /// When true, the entire generated name will be enclosed in square + /// brackets, for example: + /// + /// [MyPrefix_7ff01cb811f0_test_user_ci_agent_machine_name] + /// + /// + /// + /// A unique database object name, no more than 96 characters long. + /// + public static string GetLongName(string prefix, bool withBracket = true) + { + StringBuilder name = new(96); + + if (withBracket) + { + name.Append('['); + } + + if (prefix.Length > 32) + { + prefix = prefix.Substring(0, 32); + } + + name.Append(prefix); + name.Append('_'); + name.Append(GetGuidParts()); + name.Append('_'); + + var suffix = + Environment.UserName + '_' + + Environment.MachineName; + + int maxSuffixLength = 96 - name.Length; + if (withBracket) + { + --maxSuffixLength; + } + if (suffix.Length > maxSuffixLength) + { + suffix = suffix.Substring(0, maxSuffixLength); + } + + name.Append(suffix); + + if (withBracket) + { + name.Append(']'); + } + + return name.ToString(); } public static bool IsSupportingDistributedTransactions() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs index 977bc53257..b89cfda558 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/SqlClientCustomTokenCredential.cs @@ -16,10 +16,10 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests public class SqlClientCustomTokenCredential : TokenCredential { private const string DEFAULT_PREFIX = "/.default"; + private const string AKVKeyName = "TestSqlClientAzureKeyVaultProvider"; string _authority = ""; string _resource = ""; - string _akvUrl = ""; public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) => AcquireTokenAsync().GetAwaiter().GetResult(); @@ -31,11 +31,12 @@ private async Task AcquireTokenAsync() { // Added to reduce HttpClient calls. // For multi-user support, a better design can be implemented as needed. - if (_akvUrl != DataTestUtility.AKVUrl) + if (string.IsNullOrEmpty(_authority) || string.IsNullOrEmpty(_resource)) { using (HttpClient httpClient = new HttpClient()) { - HttpResponseMessage response = await httpClient.GetAsync(DataTestUtility.AKVUrl); + string akvUrl = new Uri(DataTestUtility.AKVBaseUri, $"/keys/{AKVKeyName}").AbsoluteUri; + HttpResponseMessage response = await httpClient.GetAsync(akvUrl); string challenge = response?.Headers.WwwAuthenticate.FirstOrDefault()?.ToString(); string trimmedChallenge = ValidateChallenge(challenge); string[] pairs = trimmedChallenge.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); @@ -66,8 +67,6 @@ private async Task AcquireTokenAsync() } } } - // Since this is a test, we only create single-instance temp cache - _akvUrl = DataTestUtility.AKVUrl; } AccessToken accessToken = await AzureActiveDirectoryAuthenticationCallback(_authority, _resource); 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 f570fad10f..7b8e551bc9 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 @@ -32,9 +32,8 @@ - - + @@ -50,6 +49,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs index 5d09be77f4..5728a8b1ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/ProviderAgnostic/ReaderTest/ReaderTest.cs @@ -19,7 +19,7 @@ public static void TestMain() { string connectionString = DataTestUtility.TCPConnectionString; - string tempTable = DataTestUtility.GetUniqueNameForSqlServer("table"); + string tempTable = DataTestUtility.GetLongName("table"); DbProviderFactory provider = SqlClientFactory.Instance; try @@ -275,7 +275,7 @@ public static void TestMain() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void SqlDataReader_SqlBuffer_GetFieldValue() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBuffer_GetFieldValue"); + string tableName = DataTestUtility.GetLongName("SqlBuffer_GetFieldValue"); DateTimeOffset dtoffset = DateTimeOffset.Now; DateTime dt = DateTime.Now; //Exclude the millisecond because of rounding at some points by SQL Server. @@ -374,7 +374,7 @@ public static void SqlDataReader_SqlBuffer_GetFieldValue() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async Task SqlDataReader_SqlBuffer_GetFieldValue_Async() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBuffer_GetFieldValue_Async"); + string tableName = DataTestUtility.GetLongName("SqlBuffer_GetFieldValue_Async"); DateTimeOffset dtoffset = DateTimeOffset.Now; DateTime dt = DateTime.Now; //Exclude the millisecond because of rounding at some points by SQL Server. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs index 9d43567cf1..371550e105 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs @@ -54,7 +54,7 @@ public class AdapterTest public AdapterTest() { // create random name for temp tables - _tempTable = DataTestUtility.GetUniqueName("AdapterTest"); + _tempTable = DataTestUtility.GetShortName("AdapterTest"); _tempTable = _tempTable.Replace('-', '_'); _randomGuid = Guid.NewGuid().ToString(); @@ -487,7 +487,7 @@ public void ParameterTest_AllTypes() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void ParameterTest_InOut() { - string procName = DataTestUtility.GetUniqueName("P"); + string procName = DataTestUtility.GetShortName("P"); // input, output string spCreateInOut = "CREATE PROCEDURE " + procName + " @in int, @inout int OUTPUT, @out nvarchar(8) OUTPUT " + @@ -768,13 +768,13 @@ public void BulkUpdateTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void UpdateRefreshTest() { - string identTableName = DataTestUtility.GetUniqueName("ID_"); + string identTableName = DataTestUtility.GetShortName("ID_"); string createIdentTable = $"CREATE TABLE {identTableName} (id int IDENTITY," + "LastName nvarchar(50) NULL," + "Firstname nvarchar(50) NULL)"; - string spName = DataTestUtility.GetUniqueName("sp_insert", withBracket: false); + string spName = DataTestUtility.GetShortName("sp_insert", withBracket: false); string spCreateInsert = $"CREATE PROCEDURE {spName}" + "(@FirstName nvarchar(50), @LastName nvarchar(50), @id int OUTPUT) " + @@ -1087,7 +1087,7 @@ public void AutoGenUpdateTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void AutoGenErrorTest() { - string identTableName = DataTestUtility.GetUniqueName("ID_"); + string identTableName = DataTestUtility.GetShortName("ID_"); string createIdentTable = $"CREATE TABLE {identTableName} (id int IDENTITY," + "LastName nvarchar(50) 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 7675643382..57d6725ce5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -368,9 +368,10 @@ public static void ConnectionOpenDisableRetry() { SqlConnectionStringBuilder connectionStringBuilder = new(DataTestUtility.TCPConnectionString) { - InitialCatalog = "DoesNotExist0982532435423", + InitialCatalog = DataTestUtility.GetLongName("DoesNotExist", false), Pooling = false, - ConnectTimeout=15 + ConnectTimeout = 15, + ConnectRetryCount = 3 }; using SqlConnection sqlConnection = new(connectionStringBuilder.ConnectionString); Stopwatch timer = new(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs index 8119bd2586..24786999e7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataClassificationTest/DataClassificationTest.cs @@ -18,7 +18,7 @@ public static class DataClassificationTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.IsSupportedDataClassification))] public static void TestDataClassificationResultSetRank() { - s_tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); + s_tableName = DataTestUtility.GetLongName("DC"); using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) using (SqlCommand sqlCommand = sqlConnection.CreateCommand()) { @@ -41,7 +41,7 @@ public static void TestDataClassificationResultSetRank() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsSupportedDataClassification))] public static void TestDataClassificationResultSet() { - s_tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); + s_tableName = DataTestUtility.GetLongName("DC"); using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) using (SqlCommand sqlCommand = sqlConnection.CreateCommand()) { @@ -232,7 +232,7 @@ public static void TestDataClassificationBulkCopy() data.Rows.Add(Guid.NewGuid(), "Company 2", "sample2@contoso.com", 1); data.Rows.Add(Guid.NewGuid(), "Company 3", "sample3@contoso.com", 1); - var tableName = DataTestUtility.GetUniqueNameForSqlServer("DC"); + var tableName = DataTestUtility.GetLongName("DC"); using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) { 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 b8731ea672..0aaaaf890d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -123,9 +123,7 @@ public static void CheckSparseColumnBit() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse))] public static void CollatedDataReaderTest() { - var databaseName = DataTestUtility.GetUniqueName("DB"); - // Remove square brackets - var dbName = databaseName.Substring(1, databaseName.Length - 2); + string dbName = DataTestUtility.GetShortName("CollationTest", false); SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString) { @@ -140,7 +138,7 @@ public static void CollatedDataReaderTest() con.Open(); // Create collated database - cmd.CommandText = $"CREATE DATABASE {databaseName} COLLATE KAZAKH_90_CI_AI"; + cmd.CommandText = $"CREATE DATABASE {dbName} COLLATE KAZAKH_90_CI_AI"; cmd.ExecuteNonQuery(); //Create connection without pooling in order to delete database later. @@ -165,7 +163,7 @@ public static void CollatedDataReaderTest() } finally { - cmd.CommandText = $"DROP DATABASE {databaseName}"; + cmd.CommandText = $"DROP DATABASE {dbName}"; cmd.ExecuteNonQuery(); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs index 30a1505442..e622121cbb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs @@ -50,7 +50,7 @@ public static async Task AsyncMultiPacketStreamRead() byte[] inputData = null; byte[] outputData = null; - string tableName = DataTestUtility.GetUniqueNameForSqlServer("data"); + string tableName = DataTestUtility.GetLongName("data"); using (SqlConnection connection = new(connectionString)) { @@ -546,7 +546,7 @@ private static void RowBuffer(string connectionString) private static void TimestampRead(string connectionString) { - string tempTable = DataTestUtility.GetUniqueNameForSqlServer("##Temp"); + string tempTable = DataTestUtility.GetLongName("##Temp"); tempTable = tempTable.Replace('-', '_'); using (SqlConnection conn = new SqlConnection(connectionString)) @@ -1041,7 +1041,7 @@ private static void SequentialAccess(string connectionString) private static void NumericRead(string connectionString) { - string tempTable = DataTestUtility.GetUniqueNameForSqlServer("##Temp"); + string tempTable = DataTestUtility.GetLongName("##Temp"); tempTable = tempTable.Replace('-', '_'); using (SqlConnection conn = new SqlConnection(connectionString)) @@ -1872,8 +1872,8 @@ private static void StreamingBlobDataTypes(string connectionString) private static void VariantCollationsTest(string connectionString) { - string dbName = DataTestUtility.GetUniqueName("JPN"); - string tableName = DataTestUtility.GetUniqueName("T"); + string dbName = DataTestUtility.GetShortName("JPN"); + string tableName = DataTestUtility.GetShortName("T"); using (SqlConnection connection = new SqlConnection(connectionString)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs index 31c232e3d0..20768e9329 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs @@ -75,7 +75,7 @@ private static void TestSimpleParameter_Type(object paramValue, string expectedT { string tag = "TestSimpleParameter_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc1"); + string procName = DataTestUtility.GetLongName("paramProc1"); try { using SqlConnection conn = new(s_connStr); @@ -115,7 +115,7 @@ private static void TestSimpleParameter_Variant(object paramValue, string expect { string tag = "TestSimpleParameter_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc2"); + string procName = DataTestUtility.GetLongName("paramProc2"); try { using SqlConnection conn = new(s_connStr); @@ -153,7 +153,7 @@ private static void TestSqlDataRecordParameterToTVP_Type(object paramValue, stri { string tag = "TestSqlDataRecordParameterToTVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); + string tvpTypeName = DataTestUtility.GetLongName("tvpType"); try { using SqlConnection conn = new(s_connStr); @@ -200,7 +200,7 @@ private static void TestSqlDataRecordParameterToTVP_Variant(object paramValue, s { string tag = "TestSqlDataRecordParameterToTVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); + string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); try { using SqlConnection conn = new(s_connStr); @@ -245,7 +245,7 @@ private static void TestSqlDataReaderParameterToTVP_Type(object paramValue, stri { string tag = "TestSqlDataReaderParameterToTVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); + string tvpTypeName = DataTestUtility.GetLongName("tvpType"); try { using SqlConnection conn = new(s_connStr); @@ -295,7 +295,7 @@ private static void TestSqlDataReaderParameterToTVP_Variant(object paramValue, s { string tag = "TestSqlDataReaderParameterToTVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); + string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); try { using SqlConnection conn = new(s_connStr); @@ -347,10 +347,10 @@ private static void TestSqlDataReader_TVP_Type(object paramValue, string expecte { string tag = "TestSqlDataReader_TVP_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpType"); - string InputTableName = DataTestUtility.GetUniqueNameForSqlServer("InputTable"); - string OutputTableName = DataTestUtility.GetUniqueNameForSqlServer("OutputTable"); - string ProcName = DataTestUtility.GetUniqueNameForSqlServer("spTVPProc"); + string tvpTypeName = DataTestUtility.GetLongName("tvpType"); + string InputTableName = DataTestUtility.GetLongName("InputTable"); + string OutputTableName = DataTestUtility.GetLongName("OutputTable"); + string ProcName = DataTestUtility.GetLongName("spTVPProc"); try { using SqlConnection conn = new(s_connStr); @@ -428,10 +428,10 @@ private static void TestSqlDataReader_TVP_Variant(object paramValue, string expe { string tag = "TestSqlDataReader_TVP_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant_DRdrTVPVar"); - string InputTableName = DataTestUtility.GetUniqueNameForSqlServer("InputTable"); - string OutputTableName = DataTestUtility.GetUniqueNameForSqlServer("OutputTable"); - string ProcName = DataTestUtility.GetUniqueNameForSqlServer("spTVPProc_DRdrTVPVar"); + string tvpTypeName = DataTestUtility.GetLongName("tvpVariant_DRdrTVPVar"); + string InputTableName = DataTestUtility.GetLongName("InputTable"); + string OutputTableName = DataTestUtility.GetLongName("OutputTable"); + string ProcName = DataTestUtility.GetLongName("spTVPProc_DRdrTVPVar"); try { using SqlConnection conn = new(s_connStr); @@ -512,8 +512,8 @@ private static void TestSimpleDataReader_Type(object paramValue, string expected { string tag = "TestSimpleDataReader_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string inputTable = DataTestUtility.GetUniqueNameForSqlServer("inputTable"); - string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc3"); + string inputTable = DataTestUtility.GetLongName("inputTable"); + string procName = DataTestUtility.GetLongName("paramProc3"); try { using SqlConnection conn = new(s_connStr); @@ -568,8 +568,8 @@ private static void TestSimpleDataReader_Variant(object paramValue, string expec { string tag = "TestSimpleDataReader_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string inputTable = DataTestUtility.GetUniqueNameForSqlServer("inputTable"); - string procName = DataTestUtility.GetUniqueNameForSqlServer("paramProc4"); + string inputTable = DataTestUtility.GetLongName("inputTable"); + string procName = DataTestUtility.GetLongName("paramProc4"); try { using SqlConnection conn = new(s_connStr); @@ -624,8 +624,8 @@ private static void SqlBulkCopySqlDataReader_Type(object paramValue, string expe { string tag = "SqlBulkCopySqlDataReader_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopySrcTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkSrcTable"); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestTable"); + string bulkCopySrcTableName = DataTestUtility.GetLongName("bulkSrcTable"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestTable"); try { using SqlConnection conn = new(s_connStr); @@ -698,8 +698,8 @@ private static void SqlBulkCopySqlDataReader_Variant(object paramValue, string e { string tag = "SqlBulkCopySqlDataReader_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopySrcTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkSrcTable"); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestTable"); + string bulkCopySrcTableName = DataTestUtility.GetLongName("bulkSrcTable"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestTable"); try { using SqlConnection conn = new(s_connStr); @@ -776,7 +776,7 @@ private static void SqlBulkCopyDataTable_Type(object paramValue, string expected { string tag = "SqlBulkCopyDataTable_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestType"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestType"); try { using SqlConnection conn = new(s_connStr); @@ -836,7 +836,7 @@ private static void SqlBulkCopyDataTable_Variant(object paramValue, string expec { string tag = "SqlBulkCopyDataTable_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestVariant"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestVariant"); try { using SqlConnection conn = new(s_connStr); @@ -886,7 +886,7 @@ private static void SqlBulkCopyDataRow_Type(object paramValue, string expectedTy { string tag = "SqlBulkCopyDataRow_Type"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestType"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestType"); try { using SqlConnection conn = new(s_connStr); @@ -941,7 +941,7 @@ private static void SqlBulkCopyDataRow_Variant(object paramValue, string expecte { string tag = "SqlBulkCopyDataRow_Variant"; DisplayHeader(tag, paramValue, expectedBaseTypeName); - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDestVariant"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDestVariant"); try { using SqlConnection conn = new(s_connStr); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index 93f31bf9f1..6136f0bfc1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -111,7 +111,7 @@ public static void CodeCoverageSqlClient() public static void Test_Copy_SqlParameter() { using var conn = new SqlConnection(s_connString); - string cTableName = DataTestUtility.GetUniqueNameForSqlServer("#tmp"); + string cTableName = DataTestUtility.GetLongName("#tmp"); try { // Create tmp table @@ -253,9 +253,9 @@ public static void TestParametersWithDatatablesTVPInsert() }; using SqlConnection connection = new(builder.ConnectionString); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Table"); - string procName = DataTestUtility.GetUniqueNameForSqlServer("Proc"); - string typeName = DataTestUtility.GetUniqueName("Type"); + string tableName = DataTestUtility.GetLongName("Table"); + string procName = DataTestUtility.GetLongName("Proc"); + string typeName = DataTestUtility.GetShortName("Type"); try { connection.Open(); @@ -360,7 +360,7 @@ public static void SqlDecimalConvertToDecimal_TestOutOfRange(string sqlDecimalVa [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalParameter_CommandInsert(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterCMD"); + string tableName = DataTestUtility.GetLongName("TestDecimalParameterCMD"); using SqlConnection connection = InitialDatabaseTable(connectionString, tableName); try { @@ -392,7 +392,7 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalParameter_BulkCopy(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterBC"); + string tableName = DataTestUtility.GetLongName("TestDecimalParameterBC"); using SqlConnection connection = InitialDatabaseTable(connectionString, tableName); try { @@ -426,9 +426,9 @@ public static void TestScaledDecimalParameter_BulkCopy(string connectionString, [ClassData(typeof(ConnectionStringsProvider))] public static void TestScaledDecimalTVP_CommandSP(string connectionString, bool truncateScaledDecimal) { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterBC"); - string tableTypeName = DataTestUtility.GetUniqueNameForSqlServer("UDTTTestDecimalParameterBC"); - string spName = DataTestUtility.GetUniqueNameForSqlServer("spTestDecimalParameterBC"); + string tableName = DataTestUtility.GetLongName("TestDecimalParameterBC"); + string tableTypeName = DataTestUtility.GetLongName("UDTTTestDecimalParameterBC"); + string spName = DataTestUtility.GetLongName("spTestDecimalParameterBC"); using SqlConnection connection = InitialDatabaseUDTT(connectionString, tableName, tableTypeName, spName); try { @@ -713,7 +713,7 @@ private static void EnableOptimizedParameterBinding_ReturnSucceeds() { int firstInput = 12; - string sprocName = DataTestUtility.GetUniqueName("P"); + string sprocName = DataTestUtility.GetShortName("P"); // input, output string createSprocQuery = "CREATE PROCEDURE " + sprocName + " @in int " + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs index 7f383e8201..aa59bc319c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlAdapterUpdateBatch.cs @@ -15,7 +15,7 @@ public class SqlAdapterUpdateBatch [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void SqlAdapterTest() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Adapter"); + string tableName = DataTestUtility.GetLongName("Adapter"); string tableNameNoBrackets = tableName.Substring(1, tableName.Length - 2); try { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs index 2d11274191..e1592825b1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/SqlVariantParam.cs @@ -108,7 +108,7 @@ private static void SendVariantParam(object paramValue, string expectedTypeName, /// private static void SendVariantBulkCopy(object paramValue, string expectedTypeName, string expectedBaseTypeName) { - string bulkCopyTableName = DataTestUtility.GetUniqueNameForSqlServer("bulkDest"); + string bulkCopyTableName = DataTestUtility.GetLongName("bulkDest"); // Fetch reader using type. using SqlDataReader dr = GetReaderForVariant(paramValue, false); @@ -194,7 +194,7 @@ private static void SendVariantBulkCopy(object paramValue, string expectedTypeNa /// private static void SendVariantTvp(object paramValue, string expectedTypeName, string expectedBaseTypeName) { - string tvpTypeName = DataTestUtility.GetUniqueNameForSqlServer("tvpVariant"); + string tvpTypeName = DataTestUtility.GetLongName("tvpVariant"); using SqlConnection connTvp = new(s_connStr); connTvp.Open(); 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 9e3ed81af4..dc467e3775 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs @@ -240,7 +240,7 @@ public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLo public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string database = DataTestUtility.GetUniqueNameForSqlServer($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); + string database = DataTestUtility.GetLongName($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); var builder = new SqlConnectionStringBuilder(cnnString) { InitialCatalog = database, @@ -302,7 +302,7 @@ public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBase public void UpdateALockedTable(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Region"); + string tableName = DataTestUtility.GetLongName("Region"); string fieldName = "RegionDescription"; using (var cnn1 = new SqlConnection(cnnString)) 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 c78c060677..d6f1e39f77 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs @@ -58,7 +58,7 @@ public void ConnectionCancelRetryOpenInvalidCatalog(string cnnString, SqlRetryLo public void CreateDatabaseWhileTryingToConnect(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; - string database = DataTestUtility.GetUniqueNameForSqlServer($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); + string database = DataTestUtility.GetLongName($"RetryLogic_{provider.RetryLogic.RetryIntervalEnumerator.GetType().Name}", false); var builder = new SqlConnectionStringBuilder(cnnString) { InitialCatalog = database, diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs index 72bab47869..a845710d50 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs @@ -41,7 +41,7 @@ public static void RunTest() private static SqlDecimal BulkCopySqlDecimalToTable(SqlDecimal decimalValue, int sourcePrecision, int sourceScale, int targetPrecision, int targetScale) { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Table"); + string tableName = DataTestUtility.GetLongName("Table"); string connectionString = DataTestUtility.TCPConnectionString; SqlDecimal resultValue; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs index 823bc50a9d..2a853d7ed4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AzureDistributedTransaction.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests public class AzureDistributedTransaction { private static readonly string s_connectionString = DataTestUtility.TCPConnectionString; - private static readonly string s_tableName = DataTestUtility.GetUniqueNameForSqlServer("Azure"); + private static readonly string s_tableName = DataTestUtility.GetLongName("Azure"); private static readonly string s_createTableCmd = $"CREATE TABLE {s_tableName} (NAME NVARCHAR(40), AGE INT)"; private static readonly string s_sqlBulkCopyCmd = "SELECT * FROM(VALUES ('Fuller', 33), ('Davon', 49)) AS q (FirstName, Age)"; private static readonly int s_commandTimeout = 30; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs index 5ccda71fb9..f961521233 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWidenNullInexactNumerics.cs @@ -12,8 +12,8 @@ public class CopyWidenNullInexactNumerics { public static void Test(string sourceDatabaseConnectionString, string destinationDatabaseConnectionString) { - string sourceTableName = DataTestUtility.GetUniqueNameForSqlServer("BCP_SRC"); - string destTableName = DataTestUtility.GetUniqueNameForSqlServer("BCP_DST"); + string sourceTableName = DataTestUtility.GetLongName("BCP_SRC"); + string destTableName = DataTestUtility.GetLongName("BCP_DST"); // this test copies float and real inexact numeric types into decimal targets using bulk copy to check that the widening of the type succeeds. using (var sourceConnection = new SqlConnection(sourceDatabaseConnectionString)) 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 4c3d594ad1..4a722dd409 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/DataConversionErrorMessageTest.cs @@ -28,7 +28,7 @@ public InitialDatabase() srcConstr = DataTestUtility.TCPConnectionString; Connection = new SqlConnection(srcConstr); - TableName = DataTestUtility.GetUniqueNameForSqlServer("SqlBulkCopyTest_CopyStringToIntTest_"); + TableName = DataTestUtility.GetLongName("SqlBulkCopyTest_CopyStringToIntTest_"); InitialTable(Connection, TableName); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs index 8e38bee7c0..21ff771ac0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCompletedTest.cs @@ -11,7 +11,7 @@ public static class SqlCommandCompletedTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void VerifyStatmentCompletedCalled() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("stmt"); + string tableName = DataTestUtility.GetLongName("stmt"); using (var conn = new SqlConnection(s_connStr)) using (var cmd = conn.CreateCommand()) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs index 26b11055c2..7f28a4a09a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandSetTest.cs @@ -15,8 +15,8 @@ public class SqlCommandSetTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public void TestByteArrayParameters() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("CMD"); - string procName = DataTestUtility.GetUniqueNameForSqlServer("CMD"); + string tableName = DataTestUtility.GetLongName("CMD"); + string procName = DataTestUtility.GetLongName("CMD"); byte[] bArray = new byte[] { 1, 2, 3 }; using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs index 09c369e11e..3bea30c6b8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs @@ -221,7 +221,7 @@ private static string SetupFileStreamDB() fileStreamDir += "\\"; } - string dbName = DataTestUtility.GetUniqueName("FS", false); + string dbName = DataTestUtility.GetShortName("FS", false); string createDBQuery = @$"CREATE DATABASE [{dbName}] ON PRIMARY (NAME = PhotoLibrary_data, @@ -266,7 +266,7 @@ private static void DropFileStreamDb(string connString) private static string SetupTable(string connString) { // Generate random table name - string tempTable = DataTestUtility.GetUniqueNameForSqlServer("fs"); + string tempTable = DataTestUtility.GetLongName("fs"); // Create table string createTable = $"CREATE TABLE {tempTable} (EmployeeId INT NOT NULL PRIMARY KEY, Photo VARBINARY(MAX) FILESTREAM NULL, RowGuid UNIQUEIDENTIFIER NOT NULL ROWGUIDCOL UNIQUE DEFAULT NEWID() ) "; ExecuteNonQueryCommand(createTable, connString); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs index 0e64b091b6..8f697af02e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs @@ -434,7 +434,7 @@ private static string GetUdtName(Type udtClrType) [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void TestSqlServerTypesInsertAndRead() { - string tableName = DataTestUtility.GetUniqueNameForSqlServer("Type"); + string tableName = DataTestUtility.GetLongName("Type"); string allTypesSQL = @$" if not exists (select * from sysobjects where name='{tableName}' and xtype='U') Begin diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs index 90ceed4952..5f17036b4f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs @@ -18,9 +18,9 @@ public void RunCopyTest() _connStr = (new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { InitialCatalog = DataTestUtility.UdtTestDbName }).ConnectionString; SqlConnection conn = new SqlConnection(_connStr); - string cities = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_cities"); - string customers = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_customers"); - string circles = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_circles"); + string cities = DataTestUtility.GetLongName("UdtBulkCopy_cities"); + string customers = DataTestUtility.GetLongName("UdtBulkCopy_customers"); + string circles = DataTestUtility.GetLongName("UdtBulkCopy_circles"); conn.Open(); try 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 1f07242ee3..ef38e43dda 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -84,8 +84,8 @@ public void UDTParams_Binary() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_Invalid2() { - string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2"); + string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetLongName("UdtTest2"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -143,8 +143,8 @@ public void UDTParams_Invalid() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_TypedNull() { - string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2_Customer"); + string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetLongName("UdtTest2_Customer"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -188,8 +188,8 @@ public void UDTParams_TypedNull() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_NullInput() { - string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2_Customer"); + string spInsertCustomer = DataTestUtility.GetLongName("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetLongName("UdtTest2_Customer"); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = conn.CreateCommand()) @@ -232,8 +232,8 @@ public void UDTParams_NullInput() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void UDTParams_InputOutput() { - string spInsertCity = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCity"); - string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2"); + string spInsertCity = DataTestUtility.GetLongName("spUdtTest2_InsertCity"); + string tableName = DataTestUtility.GetLongName("UdtTest2"); using (SqlConnection conn = new SqlConnection(_connStr)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs index effecb35b3..41f81b12e3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs @@ -37,7 +37,7 @@ public static void CheckSupportUtf8ConnectionProperty() public static void UTF8databaseTest() { const string letters = @"!\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f€\u0081‚ƒ„…†‡ˆ‰Š‹Œ\u008dŽ\u008f\u0090‘’“”•–—˜™š›œ\u009džŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"; - string dbName = DataTestUtility.GetUniqueNameForSqlServer("UTF8databaseTest", false); + string dbName = DataTestUtility.GetLongName("UTF8databaseTest", false); string tblName = "Table1"; SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/AzureKeyVaultKeyFixtureBase.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/AzureKeyVaultKeyFixtureBase.cs new file mode 100644 index 0000000000..695e3ef2ea --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/AzureKeyVaultKeyFixtureBase.cs @@ -0,0 +1,63 @@ +// 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; +using System.Collections.Generic; +using Azure.Core; +using Azure.Security.KeyVault.Keys; + +namespace Microsoft.Data.SqlClient.TestUtilities.Fixtures +{ + public abstract class AzureKeyVaultKeyFixtureBase : IDisposable + { + private readonly KeyClient _keyClient; + private readonly Random _randomGenerator; + + private readonly List _createdKeys = new List(); + + protected AzureKeyVaultKeyFixtureBase(Uri keyVaultUri, TokenCredential keyVaultToken) + { + _keyClient = new KeyClient(keyVaultUri, keyVaultToken); + _randomGenerator = new Random(); + } + + protected Uri CreateKey(string name, int keySize) + { + CreateRsaKeyOptions createOptions = new CreateRsaKeyOptions(GenerateUniqueName(name)) { KeySize = keySize }; + KeyVaultKey created = _keyClient.CreateRsaKey(createOptions); + + _createdKeys.Add(created); + return created.Id; + } + + private string GenerateUniqueName(string name) + { + byte[] rndBytes = new byte[16]; + + _randomGenerator.NextBytes(rndBytes); + return name + "-" + BitConverter.ToString(rndBytes); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + foreach (KeyVaultKey key in _createdKeys) + { + try + { + _keyClient.StartDeleteKey(key.Name).WaitForCompletion(); + } + catch(Exception) + { + continue; + } + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CertificateFixtureBase.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CertificateFixtureBase.cs new file mode 100644 index 0000000000..ba3ec7d9ed --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CertificateFixtureBase.cs @@ -0,0 +1,296 @@ +// 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.Diagnostics; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; + +namespace Microsoft.Data.SqlClient.TestUtilities.Fixtures +{ + public abstract class CertificateFixtureBase : IDisposable + { + /// + /// Certificates must be created using this provider. Certificates created by PowerShell + /// using another provider aren't accessible from RSACryptoServiceProvider, which means + /// that we could not roundtrip between SqlColumnEncryptionCertificateStoreProvider and + /// SqlColumnEncryptionCspProvider. + /// + private const string CspProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"; + + private sealed class CertificateStoreContext + { + public List Certificates { get; } + + public StoreLocation Location { get; } + + public StoreName Name { get; } + + public CertificateStoreContext(StoreLocation location, StoreName name) + { + Certificates = new List(); + Location = location; + Name = name; + } + } + + private readonly List _certificateStoreModifications = new List(); + + protected X509Certificate2 CreateCertificate(string subjectName, IEnumerable dnsNames, IEnumerable ipAddresses, bool forceCsp = false) + { + // This will always generate a certificate with: + // * Start date: 24hrs ago + // * End date: 24hrs in the future + // * Subject: {subjectName} + // * Subject alternative names: {dnsNames}, {ipAddresses} + // * Public key: 2048-bit RSA + // * Hash algorithm: SHA256 + // * Key usage: digital signature, key encipherment + // * Enhanced key usage: server authentication, client authentication + DateTimeOffset notBefore = DateTimeOffset.UtcNow.AddDays(-1); + DateTimeOffset notAfter = DateTimeOffset.UtcNow.AddDays(1); + byte[] passwordBytes = new byte[32]; + string password = null; + Random rnd = new Random(); + + rnd.NextBytes(passwordBytes); + password = Convert.ToBase64String(passwordBytes); +#if NET + X500DistinguishedName subject = new X500DistinguishedName($"CN={subjectName}"); + SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder(); + RSA rsaKey = CreateRSA(forceCsp); + + bool hasSans = false; + + foreach (string dnsName in dnsNames) + { + sanBuilder.AddDnsName(dnsName); + hasSans = true; + } + foreach (string ipAddress in ipAddresses) + { + sanBuilder.AddIpAddress(System.Net.IPAddress.Parse(ipAddress)); + hasSans = true; + } + + CertificateRequest request = new CertificateRequest(subject, rsaKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + + request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false)); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, false)); + request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection() { new Oid("1.3.6.1.5.5.7.3.1"), new Oid("1.3.6.1.5.5.7.3.2") }, true)); + + if (hasSans) + { + request.CertificateExtensions.Add(sanBuilder.Build()); + } + + // Generate an ephemeral certificate, then export it and return it as a new certificate with the correct key storage flags set. + // This is to ensure that it's imported into the certificate stores with its private key. + using (X509Certificate2 ephemeral = request.CreateSelfSigned(notBefore, notAfter)) + { + return new X509Certificate2( + ephemeral.Export(X509ContentType.Pkcs12, password), + password, + X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); + } +#else + // The CertificateRequest API is available in .NET Core, but was only added to .NET Framework 4.7.2; it thus can't be used in the test projects. + // Instead, fall back to running a PowerShell script which calls New-SelfSignedCertificate. This cmdlet also adds the certificate to a specific, + // certificate store, so remove it from there. + // Normally, the PowerShell script will return zero and print the base64-encoded certificate to stdout. If there's an exception, it'll return 1 and + // print the message instead. + const string PowerShellCommandTemplate = @"$notBefore = [DateTime]::ParseExact(""{0}"", ""O"", $null) +$notAfter = [DateTime]::ParseExact(""{1}"", ""O"", $null) +$subject = ""CN={2}"" +$sAN = @({3}) + +try +{{ + $x509 = PKI\New-SelfSignedCertificate -Subject $subject -TextExtension $sAN -KeyLength 2048 -KeyAlgorithm RSA ` + -CertStoreLocation ""Cert:\CurrentUser\My"" -NotBefore $notBefore -NotAfter $notAfter ` + -KeyExportPolicy Exportable -HashAlgorithm SHA256 -Provider ""{5}"" -KeySpec KeyExchange + + if ($x509 -eq $null) + {{ throw ""Certificate was null!"" }} + + $exportedArray = $x509.Export(""Pkcs12"", ""{4}"") + Write-Output $([Convert]::ToBase64String($exportedArray)) + + Remove-Item ""Cert:\CurrentUser\My\$($x509.Thumbprint)"" + + exit 0 +}} +catch [Exception] +{{ + Write-Output $_.Exception.Message + exit 1 +}}"; + const int PowerShellCommandTimeout = 15_000; + + string sanString = string.Empty; + bool hasSans = false; + + foreach (string dnsName in dnsNames) + { + sanString += string.Format("DNS={0}&", dnsName); + hasSans = true; + } + foreach (string ipAddress in ipAddresses) + { + sanString += string.Format("IPAddress={0}&", ipAddress); + hasSans = true; + } + + sanString = hasSans ? "\"2.5.29.17={text}" + sanString.Substring(0, sanString.Length - 1) + "\"" : string.Empty; + + string formattedCommand = string.Format(PowerShellCommandTemplate, notBefore.ToString("O"), notAfter.ToString("O"), subjectName, sanString, password, CspProviderName); + + ProcessStartInfo startInfo = new() + { + FileName = "powershell.exe", + RedirectStandardOutput = true, + RedirectStandardError = false, + UseShellExecute = false, + CreateNoWindow = true, + // Pass the Base64-encoded command to remove the need to escape quote marks + Arguments = "-EncodedCommand " + Convert.ToBase64String(Encoding.Unicode.GetBytes(formattedCommand)), + // Run as Administrator, since we're manipulating the system + // certificate store. + Verb = "RunAs", + LoadUserProfile = true + }; + + // This command sometimes fails with: + // + // Access is denied. 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED) + // + // We will retry it a few times with a short delay to avoid spurious + // failures in CI pipeline runs. + // + // See ADO issue for more details: + // + // Issue 34304: #3223 Fix Functional test failures in CI + // + // https://sqlclientdrivers.visualstudio.com/ADO.Net/_workitems/edit/34304 + // + // Delay 5 seconds between retries, and retry 3 times. + const int delay = 5; + const int retries = 3; + + string commandOutput = string.Empty; + + for (int attempt = 1; attempt <= retries; ++attempt) + { + using Process psProcess = new() { StartInfo = startInfo }; + + psProcess.Start(); + commandOutput = psProcess.StandardOutput.ReadToEnd(); + + if (!psProcess.WaitForExit(PowerShellCommandTimeout)) + { + psProcess.Kill(); + throw new Exception("Process did not complete in time, exiting."); + } + + // Process completed successfully if it had an exit code of zero, the command output will be the base64-encoded certificate + var code = psProcess.ExitCode; + if (code == 0) + { + return new X509Certificate2(Convert.FromBase64String(commandOutput), password, X509KeyStorageFlags.Exportable); + } + + Console.WriteLine( + $"PowerShell command failed with exit code {code} on " + + $"attempt {attempt} of {retries}; " + + $"retrying in {delay} seconds..."); + + Thread.Sleep(TimeSpan.FromSeconds(delay)); + } + + throw new Exception( + "PowerShell command raised exception: " + + $"{commandOutput}; command was: {formattedCommand}"); +#endif + } + +#if NET + private static RSA CreateRSA(bool forceCsp) + { + const int KeySize = 2048; + const int CspProviderType = 24; + + return forceCsp && OperatingSystem.IsWindows() + ? new RSACryptoServiceProvider(KeySize, new CspParameters(CspProviderType, CspProviderName, Guid.NewGuid().ToString())) + : RSA.Create(KeySize); + } +#endif + + protected void AddToStore(X509Certificate2 cert, StoreLocation storeLocation, StoreName storeName) + { + CertificateStoreContext storeContext = _certificateStoreModifications.Find(csc => csc.Location == storeLocation && csc.Name == storeName); + + if (storeContext == null) + { + storeContext = new(storeLocation, storeName); + _certificateStoreModifications.Add(storeContext); + } + + using X509Store store = new X509Store(storeContext.Name, storeContext.Location); + + store.Open(OpenFlags.ReadWrite); + if (store.Certificates.Contains(cert)) + { + store.Remove(cert); + } + store.Add(cert); + + storeContext.Certificates.Add(cert); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + foreach (CertificateStoreContext storeContext in _certificateStoreModifications) + { + using X509Store store = new X509Store(storeContext.Name, storeContext.Location); + + try + { + store.Open(OpenFlags.ReadWrite); + } + catch(Exception) + { + continue; + } + + foreach (X509Certificate2 cert in storeContext.Certificates) + { + try + { + if (store.Certificates.Contains(cert)) + { + store.Remove(cert); + } + } + catch (Exception) + { + continue; + } + + cert.Dispose(); + } + + storeContext.Certificates.Clear(); + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/ColumnMasterKeyCertificateFixture.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/ColumnMasterKeyCertificateFixture.cs new file mode 100644 index 0000000000..b6706be1c4 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/ColumnMasterKeyCertificateFixture.cs @@ -0,0 +1,29 @@ +// 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.X509Certificates; + +namespace Microsoft.Data.SqlClient.TestUtilities.Fixtures +{ + public class ColumnMasterKeyCertificateFixture : CertificateFixtureBase + { + public ColumnMasterKeyCertificateFixture() + : this(true) + { + } + + public X509Certificate2 ColumnMasterKeyCertificate { get; } + + protected ColumnMasterKeyCertificateFixture(bool createCertificate) + { + if (createCertificate) + { + ColumnMasterKeyCertificate = CreateCertificate(nameof(ColumnMasterKeyCertificate), Array.Empty(), Array.Empty()); + + AddToStore(ColumnMasterKeyCertificate, StoreLocation.CurrentUser, StoreName.My); + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CspCertificateFixture.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CspCertificateFixture.cs new file mode 100644 index 0000000000..7fabaf1b9c --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CspCertificateFixture.cs @@ -0,0 +1,47 @@ +// 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.TestUtilities.Fixtures +{ + public class CspCertificateFixture : CertificateFixtureBase + { + public CspCertificateFixture() + { + CspCertificate = CreateCertificate(nameof(CspCertificate), Array.Empty(), Array.Empty(), true); + + AddToStore(CspCertificate, StoreLocation.CurrentUser, StoreName.My); + + CspCertificatePath = string.Format("{0}/{1}/{2}", StoreLocation.CurrentUser, StoreName.My, CspCertificate.Thumbprint); + CspKeyPath = GetCspPathFromCertificate(); + } + + public X509Certificate2 CspCertificate { get; } + + public string CspCertificatePath { get; } + + public string CspKeyPath { get; } + + private string GetCspPathFromCertificate() + { + RSA privateKey = CspCertificate.GetRSAPrivateKey(); + + if (privateKey is RSACryptoServiceProvider csp) + { + return string.Concat(csp.CspKeyContainerInfo.ProviderName, @"/", csp.CspKeyContainerInfo.KeyContainerName); + } + else if (privateKey is RSACng cng) + { + return string.Concat(cng.Key.Provider.Provider, @"/", cng.Key.KeyName); + } + else + { + return null; + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj index 3bd48830cc..379dd4cb96 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj @@ -1,6 +1,9 @@ - netstandard2.0 + netfx + netcoreapp + win + win-$(Platform) $(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName) $(BinFolder)$(Configuration).$(Platform)\$(AssemblyName) @@ -11,6 +14,8 @@ PreserveNewest + + \ No newline at end of file diff --git a/tools/props/Versions.props b/tools/props/Versions.props index a2b1d7273a..0aca13245e 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -66,6 +66,7 @@ 13.0.1 4.3.0 6.0.1 + 5.0.0 2.4.2 2.4.5 7.0.0-beta.22316.1 From d6beb75005eb8b12481b98b400718a28d50e71e6 Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Thu, 23 Oct 2025 11:46:34 -0500 Subject: [PATCH 65/76] Replace non-compliant code with compliant code (#3465) --- .../Data/SqlTypes/SqlTypeWorkarounds.netfx.cs | 183 +++++------------- 1 file changed, 46 insertions(+), 137 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs index 941e3325b6..af7269ed8b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs @@ -5,8 +5,6 @@ using System; using System.Data.SqlTypes; using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.Serialization; using Microsoft.Data.SqlClient; namespace Microsoft.Data.SqlTypes @@ -20,7 +18,10 @@ namespace Microsoft.Data.SqlTypes internal static partial class SqlTypeWorkarounds { #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation - private static readonly Func s_sqlMoneyfactory = CtorHelper.CreateFactory(); // binds to SqlMoney..ctor(long, int) if it exists + // Documentation for internal ctor: + // https://learn.microsoft.com/en-us/dotnet/framework/additional-apis/system.data.sqltypes.sqlmoney.-ctor + private static readonly Func s_sqlMoneyfactory = + CtorHelper.CreateFactory(); // binds to SqlMoney..ctor(long, int) if it exists /// /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists @@ -70,6 +71,11 @@ internal static SqlMoneyToLongDelegate GetSqlMoneyToLong() private static SqlMoneyToLongDelegate GetFastSqlMoneyToLong() { + // Note: Although it would be faster to use the m_value member variable in + // SqlMoney, but because it is not documented, we cannot use it. The method + // we are calling below *is* documented, despite it being internal. + // Documentation for internal method: + // https://learn.microsoft.com/en-us/dotnet/framework/additional-apis/system.data.sqltypes.sqlmoney.tosqlinternalrepresentation MethodInfo toSqlInternalRepresentation = typeof(SqlMoney).GetMethod("ToSqlInternalRepresentation", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding, null, CallingConventions.Any, new Type[] { }, null); @@ -113,145 +119,45 @@ private static long FallbackSqlMoneyToLong(ref SqlMoney value) } #endregion - #region Work around inability to access SqlDecimal._data1/2/3/4 - internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4) - { - SqlDecimalHelper.s_decompose(d, out data1, out data2, out data3, out data4); - } + #region Work around SqlDecimal.WriteTdsValue not existing in netfx - private static class SqlDecimalHelper + /// + /// Implementation that mimics netcore's WriteTdsValue method. + /// + /// + /// Although calls to this method could just be replaced with calls to + /// , using this mimic method allows netfx and netcore + /// implementations to be more cleanly switched. + /// + /// SqlDecimal value to get data from. + /// First data field will be written here. + /// Second data field will be written here. + /// Third data field will be written here. + /// Fourth data field will be written here. + internal static void SqlDecimalExtractData( + SqlDecimal value, + out uint data1, + out uint data2, + out uint data3, + out uint data4) { - internal delegate void Decomposer(SqlDecimal value, out uint data1, out uint data2, out uint data3, out uint data4); - internal static readonly Decomposer s_decompose = GetDecomposer(); - - private static Decomposer GetDecomposer() - { - Decomposer decomposer = null; - try - { - decomposer = GetFastDecomposer(); - } - catch - { - // If an exception occurs for any reason, swallow & use the fallback code path. - } - - return decomposer ?? FallbackDecomposer; - } - - private static Decomposer GetFastDecomposer() - { - // This takes advantage of the fact that for [Serializable] types, the member fields are implicitly - // part of the type's serialization contract. This includes the fields' names and types. By default, - // [Serializable]-compliant serializers will read all the member fields and shove the data into a - // SerializationInfo dictionary. We mimic this behavior in a manner consistent with the [Serializable] - // pattern, but much more efficiently. - // - // In order to make sure we're staying compliant, we need to gate our checks to fulfill some core - // assumptions. Importantly, the type must be [Serializable] but cannot be ISerializable, as the - // presence of the interface means that the type wants to be responsible for its own serialization, - // and that member fields are not guaranteed to be part of the serialization contract. Additionally, - // we need to check for [OnSerializing] and [OnDeserializing] methods, because we cannot account - // for any logic which might be present within them. - - if (!typeof(SqlDecimal).IsSerializable) - { - SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal isn't Serializable. Less efficient fallback method will be used."); - return null; // type is not serializable - cannot use fast path assumptions - } - - if (typeof(ISerializable).IsAssignableFrom(typeof(SqlDecimal))) - { - SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal is ISerializable. Less efficient fallback method will be used."); - return null; // type contains custom logic - cannot use fast path assumptions - } - - foreach (MethodInfo method in typeof(SqlDecimal).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) - { - if (method.IsDefined(typeof(OnDeserializingAttribute)) || method.IsDefined(typeof(OnDeserializedAttribute))) - { - SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal contains custom serialization logic. Less efficient fallback method will be used."); - return null; // type contains custom logic - cannot use fast path assumptions - } - } - - // GetSerializableMembers filters out [NonSerialized] fields for us automatically. - - FieldInfo fiData1 = null, fiData2 = null, fiData3 = null, fiData4 = null; - foreach (MemberInfo candidate in FormatterServices.GetSerializableMembers(typeof(SqlDecimal))) - { - if (candidate is FieldInfo fi && fi.FieldType == typeof(uint)) - { - if (fi.Name == "m_data1") - { fiData1 = fi; } - else if (fi.Name == "m_data2") - { fiData2 = fi; } - else if (fi.Name == "m_data3") - { fiData3 = fi; } - else if (fi.Name == "m_data4") - { fiData4 = fi; } - } - } - - if (fiData1 is null || fiData2 is null || fiData3 is null || fiData4 is null) - { - SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | Expected SqlDecimal fields are missing. Less efficient fallback method will be used."); - return null; // missing one of the expected member fields - cannot use fast path assumptions - } - - Type refToUInt32 = typeof(uint).MakeByRefType(); - DynamicMethod dm = new( - name: "sqldecimal-decomposer", - returnType: typeof(void), - parameterTypes: new[] { typeof(SqlDecimal), refToUInt32, refToUInt32, refToUInt32, refToUInt32 }, - restrictedSkipVisibility: true); // perf: JITs method at delegate creation time - - ILGenerator ilGen = dm.GetILGenerator(); - ilGen.Emit(OpCodes.Ldarg_1); // eval stack := [UInt32&] - ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal] - ilGen.Emit(OpCodes.Ldfld, fiData1); // eval stack := [UInt32&] [UInt32] - ilGen.Emit(OpCodes.Stind_I4); // eval stack := - ilGen.Emit(OpCodes.Ldarg_2); // eval stack := [UInt32&] - ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal] - ilGen.Emit(OpCodes.Ldfld, fiData2); // eval stack := [UInt32&] [UInt32] - ilGen.Emit(OpCodes.Stind_I4); // eval stack := - ilGen.Emit(OpCodes.Ldarg_3); // eval stack := [UInt32&] - ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal] - ilGen.Emit(OpCodes.Ldfld, fiData3); // eval stack := [UInt32&] [UInt32] - ilGen.Emit(OpCodes.Stind_I4); // eval stack := - ilGen.Emit(OpCodes.Ldarg_S, (byte)4); // eval stack := [UInt32&] - ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal] - ilGen.Emit(OpCodes.Ldfld, fiData4); // eval stack := [UInt32&] [UInt32] - ilGen.Emit(OpCodes.Stind_I4); // eval stack := - ilGen.Emit(OpCodes.Ret); - - return (Decomposer)dm.CreateDelegate(typeof(Decomposer), null /* target */); - } - - // Used in case we can't use a [Serializable]-like mechanism. - private static void FallbackDecomposer(SqlDecimal value, out uint data1, out uint data2, out uint data3, out uint data4) - { - if (value.IsNull) - { - data1 = default; - data2 = default; - data3 = default; - data4 = default; - } - else - { - int[] data = value.Data; // allocation - data4 = (uint)data[3]; // write in reverse to avoid multiple bounds checks - data3 = (uint)data[2]; - data2 = (uint)data[1]; - data1 = (uint)data[0]; - } - } + // Note: Although it would be faster to use the m_data[1-4] member variables in + // SqlDecimal, we cannot use them because they are not documented. The Data property + // is less ideal, but is documented. + int[] data = value.Data; + data1 = (uint)data[0]; + data2 = (uint)data[1]; + data3 = (uint)data[2]; + data4 = (uint)data[3]; } + #endregion #region Work around inability to access SqlBinary.ctor(byte[], bool) - private static readonly Func s_sqlBinaryfactory = CtorHelper.CreateFactory(); // binds to SqlBinary..ctor(byte[], bool) if it exists + // Documentation of internal constructor: + // https://learn.microsoft.com/en-us/dotnet/framework/additional-apis/system.data.sqltypes.sqlbinary.-ctor + private static readonly Func s_sqlBinaryfactory = + CtorHelper.CreateFactory(); internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored) { @@ -270,7 +176,10 @@ internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored) #endregion #region Work around inability to access SqlGuid.ctor(byte[], bool) - private static readonly Func s_sqlGuidfactory = CtorHelper.CreateFactory(); // binds to SqlGuid..ctor(byte[], bool) if it exists + // Documentation for internal constructor: + // https://learn.microsoft.com/en-us/dotnet/framework/additional-apis/system.data.sqltypes.sqlguid.-ctor + private static readonly Func s_sqlGuidfactory = + CtorHelper.CreateFactory(); internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored) { From 11c1f15d5174fbb703f97e322f6541d2c496f2f3 Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Tue, 28 Oct 2025 13:52:40 -0700 Subject: [PATCH 66/76] [5.1] Backport BAG failover fix. Ignore server provided failover partner. (#3704) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #3400, ignore server-provided failover partner (#3625) * Add IgnoreServerProvidedFailoverPartner app context switch. * Add behavior skip to netfx. * Consolidate to single property for failover partner value. * Rework checks to preserve server provided value, but ignore it. * Fix import. * Skip server failover partner override in ŒLoginNoFailover method. Add simulated server test coverage. * Fix compilation issues. * Clean up duplicate line. Adjust app context switch logic to match existing. * Review changes. * Add test case. Doesn't compile. * Adjust usage of test servers and app context. * Add failover and prelogin count capabilities to test server. Update test case. * Set backing field using reflection. * Try clearing pool to make test more reliable. * Use string.Empty --- .../Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../SqlClient/SqlInternalConnectionTds.cs | 59 ++++++++----- .../Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../SqlClient/SqlInternalConnectionTds.cs | 68 ++++++++------- .../Data/SqlClient/LocalAppContextSwitches.cs | 25 ++++++ .../SqlConnectionBasicTests.cs | 83 +++++++++++++++++++ .../tools/TDS/TDS.Servers/GenericTDSServer.cs | 24 +++++- .../TDS/TDS.Servers/TDSServerArguments.cs | 5 ++ 8 files changed, 213 insertions(+), 55 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 f3b26e7899..bf4e9cf1cc 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 @@ -3431,7 +3431,7 @@ private void CheckNotificationStateAndAutoEnlist() } Notification.Options = SqlDependency.GetDefaultComposedOptions(_activeConnection.DataSource, - InternalTdsConnection.ServerProvidedFailOverPartner, + InternalTdsConnection.ServerProvidedFailoverPartner, identityUserName, _activeConnection.Database); } 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 49c0883e3d..d024c96a7d 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 @@ -300,7 +300,6 @@ internal SessionData CurrentSessionData // FOR CONNECTION RESET MANAGEMENT private bool _fResetConnection; private string _originalDatabase; - private string _currentFailoverPartner; // only set by ENV change from server private string _originalLanguage; private string _currentLanguage; private int _currentPacketSize; @@ -667,13 +666,7 @@ internal TdsParser Parser } } - internal string ServerProvidedFailOverPartner - { - get - { - return _currentFailoverPartner; - } - } + internal string ServerProvidedFailoverPartner { get; private set; } internal SqlConnectionPoolGroupProviderInfo PoolGroupProviderInfo { @@ -1533,7 +1526,7 @@ private void LoginNoFailover(ServerInfo serverInfo, !connectionOptions.MultiSubnetFailover, // ignore timeout for SniOpen call unless MSF connectionOptions.MultiSubnetFailover ? intervalTimer : timeout); - if (connectionOptions.MultiSubnetFailover && null != ServerProvidedFailOverPartner) + if (connectionOptions.MultiSubnetFailover && ServerProvidedFailoverPartner != null) { // connection succeeded: trigger exception if server sends failover partner and MultiSubnetFailover is used throw SQL.MultiSubnetFailoverWithFailoverPartner(serverProvidedFailoverPartner: true, internalConnection: this); @@ -1561,7 +1554,7 @@ private void LoginNoFailover(ServerInfo serverInfo, _currentPacketSize = ConnectionOptions.PacketSize; _currentLanguage = _originalLanguage = ConnectionOptions.CurrentLanguage; CurrentDatabase = _originalDatabase = ConnectionOptions.InitialCatalog; - _currentFailoverPartner = null; + ServerProvidedFailoverPartner = null; _instanceName = string.Empty; routingAttempts++; @@ -1599,7 +1592,7 @@ private void LoginNoFailover(ServerInfo serverInfo, // We only get here when we failed to connect, but are going to re-try // Switch to failover logic if the server provided a partner - if (null != ServerProvidedFailOverPartner) + if (ServerProvidedFailoverPartner != null) { if (connectionOptions.MultiSubnetFailover) { @@ -1615,7 +1608,7 @@ private void LoginNoFailover(ServerInfo serverInfo, LoginWithFailover( true, // start by using failover partner, since we already failed to connect to the primary serverInfo, - ServerProvidedFailOverPartner, + ServerProvidedFailoverPartner, newPassword, newSecurePassword, redirectedUserInstance, @@ -1637,8 +1630,13 @@ private void LoginNoFailover(ServerInfo serverInfo, { // We must wait for CompleteLogin to finish for to have the // env change from the server to know its designated failover - // partner; save this information in _currentFailoverPartner. - PoolGroupProviderInfo.FailoverCheck(false, connectionOptions, ServerProvidedFailOverPartner); + // partner; save this information in ServerProvidedFailoverPartner. + + // When ignoring server provided failover partner, we must pass in the original failover partner from the connection string. + // Otherwise the pool group's failover partner designation will be updated to point to the server provided value. + string actualFailoverPartner = LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner ? string.Empty : ServerProvidedFailoverPartner; + + PoolGroupProviderInfo.FailoverCheck(false, connectionOptions, actualFailoverPartner); } CurrentDataSource = originalServerInfo.UserServerName; } @@ -1699,7 +1697,7 @@ TimeoutTimer timeout ServerInfo failoverServerInfo = new ServerInfo(connectionOptions, failoverHost, connectionOptions.FailoverPartnerSPN); ResolveExtendedServerName(primaryServerInfo, !redirectedUserInstance, connectionOptions); - if (null == ServerProvidedFailOverPartner) + if (ServerProvidedFailoverPartner == null) { ResolveExtendedServerName(failoverServerInfo, !redirectedUserInstance && failoverHost != primaryServerInfo.UserServerName, connectionOptions); } @@ -1747,12 +1745,21 @@ TimeoutTimer timeout ServerInfo currentServerInfo; if (useFailoverHost) { - // Primary server may give us a different failover partner than the connection string indicates. Update it - if (null != ServerProvidedFailOverPartner && failoverServerInfo.ResolvedServerName != ServerProvidedFailOverPartner) + // Primary server may give us a different failover partner than the connection string indicates. + // Update it only if we are respecting server-provided failover partner values. + if (ServerProvidedFailoverPartner != null && failoverServerInfo.ResolvedServerName != ServerProvidedFailoverPartner) { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, new failover partner={1}", ObjectID, ServerProvidedFailOverPartner); - failoverServerInfo.SetDerivedNames(string.Empty, ServerProvidedFailOverPartner); + if (LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner) + { + SqlClientEventSource.Log.TryTraceEvent(" {0}, Ignoring server provided failover partner '{1}' due to IgnoreServerProvidedFailoverPartner AppContext switch.", ObjectID, ServerProvidedFailoverPartner); + } + else + { + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, new failover partner={1}", ObjectID, ServerProvidedFailoverPartner); + failoverServerInfo.SetDerivedNames(string.Empty, ServerProvidedFailoverPartner); + } } + currentServerInfo = failoverServerInfo; _timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.Failover); } @@ -1834,7 +1841,7 @@ TimeoutTimer timeout _activeDirectoryAuthTimeoutRetryHelper.State = ActiveDirectoryAuthenticationTimeoutRetryState.HasLoggedIn; // if connected to failover host, but said host doesn't have DbMirroring set up, throw an error - if (useFailoverHost && null == ServerProvidedFailOverPartner) + if (useFailoverHost && ServerProvidedFailoverPartner == null) { throw SQL.InvalidPartnerConfiguration(failoverHost, CurrentDatabase); } @@ -1843,8 +1850,13 @@ TimeoutTimer timeout { // We must wait for CompleteLogin to finish for to have the // env change from the server to know its designated failover - // partner; save this information in _currentFailoverPartner. - PoolGroupProviderInfo.FailoverCheck(useFailoverHost, connectionOptions, ServerProvidedFailOverPartner); + // partner. + + // When ignoring server provided failover partner, we must pass in the original failover partner from the connection string. + // Otherwise the pool group's failover partner designation will be updated to point to the server provided value. + string actualFailoverPartner = LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner ? failoverHost : ServerProvidedFailoverPartner; + + PoolGroupProviderInfo.FailoverCheck(useFailoverHost, connectionOptions, actualFailoverPartner); } CurrentDataSource = (useFailoverHost ? failoverHost : primaryServerInfo.UserServerName); } @@ -2068,7 +2080,8 @@ internal void OnEnvChange(SqlEnvChange rec) { throw SQL.ROR_FailoverNotSupportedServer(this); } - _currentFailoverPartner = rec._newValue; + + ServerProvidedFailoverPartner = rec._newValue; break; case TdsEnums.ENV_PROMOTETRANSACTION: 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 6922be9735..fae38d0012 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 @@ -3814,7 +3814,7 @@ private void CheckNotificationStateAndAutoEnlist() } Notification.Options = SqlDependency.GetDefaultComposedOptions(_activeConnection.DataSource, - InternalTdsConnection.ServerProvidedFailOverPartner, + InternalTdsConnection.ServerProvidedFailoverPartner, identityUserName, _activeConnection.Database); } 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 748483b49d..d39591fcf9 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 @@ -277,7 +277,6 @@ internal SessionData CurrentSessionData // FOR CONNECTION RESET MANAGEMENT private bool _fResetConnection; private string _originalDatabase; - private string _currentFailoverPartner; // only set by ENV change from server private string _originalLanguage; private string _currentLanguage; private int _currentPacketSize; @@ -777,13 +776,7 @@ internal TdsParser Parser } } - internal string ServerProvidedFailOverPartner - { - get - { - return _currentFailoverPartner; - } - } + internal string ServerProvidedFailoverPartner { get; private set; } internal SqlConnectionPoolGroupProviderInfo PoolGroupProviderInfo { @@ -1707,7 +1700,7 @@ private void OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectio throw SQL.ROR_FailoverNotSupportedConnString(); } - if (null != ServerProvidedFailOverPartner) + if (ServerProvidedFailoverPartner != null) { throw SQL.ROR_FailoverNotSupportedServer(this); } @@ -1859,7 +1852,7 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt isFirstTransparentAttempt: isFirstTransparentAttempt, disableTnir: disableTnir); - if (connectionOptions.MultiSubnetFailover && null != ServerProvidedFailOverPartner) + if (connectionOptions.MultiSubnetFailover && ServerProvidedFailoverPartner != null) { // connection succeeded: trigger exception if server sends failover partner and MultiSubnetFailover is used. throw SQL.MultiSubnetFailoverWithFailoverPartner(serverProvidedFailoverPartner: true, internalConnection: this); @@ -1888,8 +1881,8 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt _currentPacketSize = ConnectionOptions.PacketSize; _currentLanguage = _originalLanguage = ConnectionOptions.CurrentLanguage; CurrentDatabase = _originalDatabase = ConnectionOptions.InitialCatalog; - _currentFailoverPartner = null; - _instanceName = String.Empty; + ServerProvidedFailoverPartner = null; + _instanceName = string.Empty; routingAttempts++; @@ -1928,7 +1921,7 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt // We only get here when we failed to connect, but are going to re-try // Switch to failover logic if the server provided a partner - if (null != ServerProvidedFailOverPartner) + if (ServerProvidedFailoverPartner != null) { if (connectionOptions.MultiSubnetFailover) { @@ -1944,7 +1937,7 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt LoginWithFailover( true, // start by using failover partner, since we already failed to connect to the primary serverInfo, - ServerProvidedFailOverPartner, + ServerProvidedFailoverPartner, newPassword, newSecurePassword, redirectedUserInstance, @@ -1967,8 +1960,13 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt { // We must wait for CompleteLogin to finish for to have the // env change from the server to know its designated failover - // partner; save this information in _currentFailoverPartner. - PoolGroupProviderInfo.FailoverCheck(false, connectionOptions, ServerProvidedFailOverPartner); + // partner; save this information in ServerProvidedFailoverPartner. + + // When ignoring server provided failover partner, we must pass in the original failover partner from the connection string. + // Otherwise the pool group's failover partner designation will be updated to point to the server provided value. + string actualFailoverPartner = LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner ? string.Empty : ServerProvidedFailoverPartner; + + PoolGroupProviderInfo.FailoverCheck(false, connectionOptions, actualFailoverPartner); } CurrentDataSource = originalServerInfo.UserServerName; } @@ -2052,7 +2050,7 @@ TimeoutTimer timeout ServerInfo failoverServerInfo = new ServerInfo(connectionOptions, failoverHost, connectionOptions.FailoverPartnerSPN); ResolveExtendedServerName(primaryServerInfo, !redirectedUserInstance, connectionOptions); - if (null == ServerProvidedFailOverPartner) + if (ServerProvidedFailoverPartner == null) {// No point in resolving the failover partner when we're going to override it below // Don't resolve aliases if failover == primary // UNDONE: WHY? Previous code in TdsParser.Connect did this, but the reason is not clear ResolveExtendedServerName(failoverServerInfo, !redirectedUserInstance && failoverHost != primaryServerInfo.UserServerName, connectionOptions); @@ -2107,13 +2105,21 @@ TimeoutTimer timeout FailoverPermissionDemand(); failoverDemandDone = true; } - - // Primary server may give us a different failover partner than the connection string indicates. Update it - if (null != ServerProvidedFailOverPartner && failoverServerInfo.ResolvedServerName != ServerProvidedFailOverPartner) + // Primary server may give us a different failover partner than the connection string indicates. + // Update it only if we are respecting server-provided failover partner values. + if (ServerProvidedFailoverPartner != null && failoverServerInfo.ResolvedServerName != ServerProvidedFailoverPartner) { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, new failover partner={1}", ObjectID, ServerProvidedFailOverPartner); - failoverServerInfo.SetDerivedNames(protocol, ServerProvidedFailOverPartner); + if (LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner) + { + SqlClientEventSource.Log.TryTraceEvent(" {0}, Ignoring server provided failover partner '{1}' due to IgnoreServerProvidedFailoverPartner AppContext switch.", ObjectID, ServerProvidedFailoverPartner); + } + else + { + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, new failover partner={1}", ObjectID, ServerProvidedFailoverPartner); + failoverServerInfo.SetDerivedNames(protocol, ServerProvidedFailoverPartner); + } } + currentServerInfo = failoverServerInfo; timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.Failover); } @@ -2161,8 +2167,8 @@ TimeoutTimer timeout _currentPacketSize = ConnectionOptions.PacketSize; _currentLanguage = _originalLanguage = ConnectionOptions.CurrentLanguage; CurrentDatabase = _originalDatabase = ConnectionOptions.InitialCatalog; - _currentFailoverPartner = null; - _instanceName = String.Empty; + ServerProvidedFailoverPartner = null; + _instanceName = string.Empty; AttemptOneLogin( currentServerInfo, @@ -2226,8 +2232,7 @@ TimeoutTimer timeout // If we get here, connection/login succeeded! Just a few more checks & record-keeping _activeDirectoryAuthTimeoutRetryHelper.State = ActiveDirectoryAuthenticationTimeoutRetryState.HasLoggedIn; - // if connected to failover host, but said host doesn't have DbMirroring set up, throw an error - if (useFailoverHost && null == ServerProvidedFailOverPartner) + if (useFailoverHost && ServerProvidedFailoverPartner == null) { throw SQL.InvalidPartnerConfiguration(failoverHost, CurrentDatabase); } @@ -2236,8 +2241,13 @@ TimeoutTimer timeout { // We must wait for CompleteLogin to finish for to have the // env change from the server to know its designated failover - // partner; save this information in _currentFailoverPartner. - PoolGroupProviderInfo.FailoverCheck(useFailoverHost, connectionOptions, ServerProvidedFailOverPartner); + // partner. + + // When ignoring server provided failover partner, we must pass in the original failover partner from the connection string. + // Otherwise the pool group's failover partner designation will be updated to point to the server provided value. + string actualFailoverPartner = LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner ? failoverHost : ServerProvidedFailoverPartner; + + PoolGroupProviderInfo.FailoverCheck(useFailoverHost, connectionOptions, actualFailoverPartner); } CurrentDataSource = (useFailoverHost ? failoverHost : primaryServerInfo.UserServerName); } @@ -2500,7 +2510,7 @@ internal void OnEnvChange(SqlEnvChange rec) break; case TdsEnums.ENV_LOGSHIPNODE: - _currentFailoverPartner = rec._newValue; + ServerProvidedFailoverPartner = rec._newValue; break; case TdsEnums.ENV_PROMOTETRANSACTION: 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 1791ad5d52..3270afaae9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs @@ -14,10 +14,12 @@ internal static partial class LocalAppContextSwitches internal const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking"; internal const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"; internal const string SuppressInsecureTLSWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning"; + private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner"; private static bool s_makeReadAsyncBlocking; private static bool? s_LegacyRowVersionNullBehavior; private static bool? s_SuppressInsecureTLSWarning; + private static bool? s_ignoreServerProvidedFailoverPartner; #if !NETFRAMEWORK static LocalAppContextSwitches() @@ -76,5 +78,28 @@ public static bool LegacyRowVersionNullBehavior return s_LegacyRowVersionNullBehavior.Value; } } + + /// + /// When set to true, the failover partner provided by the server during connection + /// will be ignored. This is useful in scenarios where the application wants to + /// control the failover behavior explicitly (e.g. using a custom port). The application + /// must be kept up to date with the failover configuration of the server. + /// The application will not automatically discover a newly configured failover partner. + /// + /// This app context switch defaults to 'false'. + /// + public static bool IgnoreServerProvidedFailoverPartner + { + get + { + if (s_ignoreServerProvidedFailoverPartner is null) + { + bool result; + result = AppContext.TryGetSwitch(IgnoreServerProvidedFailoverPartnerString, out result) ? result : false; + s_ignoreServerProvidedFailoverPartner = result; + } + return s_ignoreServerProvidedFailoverPartner.Value; + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index e3183ee6a0..accb6f2201 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -3,11 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Configuration; using System.Data; using System.Data.Common; using System.Reflection; using System.Security; using System.Threading.Tasks; +using Microsoft.SqlServer.TDS.EndPoint; using Microsoft.SqlServer.TDS.Servers; using Xunit; @@ -15,6 +17,17 @@ namespace Microsoft.Data.SqlClient.Tests { public class SqlConnectionBasicTests { + // Reflection + public static Assembly systemData = Assembly.GetAssembly(typeof(SqlConnection)); + + public static Type sqlConnection = systemData.GetType("Microsoft.Data.SqlClient.SqlConnection"); + public static PropertyInfo innerConnectionProperty = sqlConnection.GetProperty("InnerConnection", BindingFlags.NonPublic | BindingFlags.Instance); + public static Type sqlInternalConnectionTds = systemData.GetType("Microsoft.Data.SqlClient.SqlInternalConnectionTds"); + public static PropertyInfo serverProvidedFailoverPartnerProperty = sqlInternalConnectionTds.GetProperty("ServerProvidedFailoverPartner", BindingFlags.NonPublic | BindingFlags.Instance); + public static Type localAppContextSwitches = systemData.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches"); + public static FieldInfo ignoreServerProvidedFailoverPartnerField = localAppContextSwitches.GetField("s_ignoreServerProvidedFailoverPartner", BindingFlags.NonPublic | BindingFlags.Static); + + [Fact] public void ConnectionTest() { @@ -207,5 +220,75 @@ public void ConnectionTestValidCredentialCombination() Assert.Equal(sqlCredential, conn.Credential); } + + [Fact] + public void TransientFault_IgnoreServerProvidedFailoverPartner_ShouldConnectToUserProvidedPartner() + { + // Arrange + ignoreServerProvidedFailoverPartnerField.SetValue(null, true); + + + try + { + using TestTdsServer failoverServer = TestTdsServer.StartTestServer(); + // Doesn't need to point to a real endpoint, just needs a value specified + failoverServer.Arguments.FailoverPartner = "localhost,1234"; + + var failoverBuilder = new SqlConnectionStringBuilder(failoverServer.ConnectionString); + + using TestTdsServer server = TestTdsServer.StartTestServer(); + // Set an invalid failover partner to ensure that the connection fails if the + // server provided failover partner is used. + server.Arguments.FailoverPartner = $"invalidhost"; + + SqlConnectionStringBuilder builder = new(server.ConnectionString) + { + InitialCatalog = "master", + Encrypt = false, + FailoverPartner = failoverBuilder.DataSource, + // Ensure pooling is enabled so that the failover partner information + // is persisted in the pool group. If pooling is disabled, the server + // provided failover partner will never be used. + Pooling = true, + MinPoolSize = 1, + }; + SqlConnection connection = new(builder.ConnectionString); + + // Connect once to the primary to trigger it to send the failover partner + connection.Open(); + + var innerConnection = innerConnectionProperty.GetValue(connection); + var serverProvidedFailoverPartner = serverProvidedFailoverPartnerProperty.GetValue(innerConnection); + Assert.Equal("invalidhost", serverProvidedFailoverPartner); + + // Close the connection to return it to the pool + connection.Close(); + + // Act + // Dispose of the server to trigger a failover + server.Dispose(); + + // Opening a new connection will use the failover partner stored in the pool group. + // This will fail if the server provided failover partner was stored to the pool group. + using SqlConnection failoverConnection = new(builder.ConnectionString); + + // Clear the pool to ensure a new physical connection is created + // Pool group info such as failover partner will still be retained + SqlConnection.ClearPool(connection); + failoverConnection.Open(); + + // Assert + Assert.Equal(ConnectionState.Open, failoverConnection.State); + Assert.Equal(failoverBuilder.DataSource, failoverConnection.DataSource); + // 1 for the initial connection + Assert.Equal(1, server.PreLoginCount); + // 1 for the failover connection + Assert.Equal(1, failoverServer.PreLoginCount); + } + finally + { + ignoreServerProvidedFailoverPartnerField.SetValue(null, false); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs index 535304964d..09e51558a8 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs @@ -32,16 +32,26 @@ public class GenericTDSServer : ITDSServer /// private int _sessionCount = 0; + /// + /// Counts pre-login requests to the server. + /// + private int _preLoginCount = 0; + /// /// Server configuration /// - protected TDSServerArguments Arguments { get; set; } + public TDSServerArguments Arguments { get; set; } /// /// Query engine instance /// protected QueryEngine Engine { get; set; } + /// + /// Counts pre-login requests to the server. + /// + public int PreLoginCount => _preLoginCount; + /// /// Default constructor /// @@ -104,6 +114,8 @@ public virtual void CloseSession(ITDSServerSession session) /// public virtual TDSMessageCollection OnPreLoginRequest(ITDSServerSession session, TDSMessage request) { + Interlocked.Increment(ref _preLoginCount); + // Inflate pre-login request from the message TDSPreLoginToken preLoginRequest = request[0] as TDSPreLoginToken; @@ -544,6 +556,16 @@ protected virtual TDSMessageCollection OnAuthenticationCompleted(ITDSServerSessi responseMessage.Add(featureExtActToken); } + if (!string.IsNullOrEmpty(Arguments.FailoverPartner)) + { + envChange = new TDSEnvChangeToken(TDSEnvChangeTokenType.RealTimeLogShipping, Arguments.FailoverPartner); + + // Log response + TDSUtilities.Log(Arguments.Log, "Response", envChange); + + responseMessage.Add(envChange); + } + // Create DONE token TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final); diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs index 1543ebde63..341bc3e46a 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs @@ -69,6 +69,11 @@ public class TDSServerArguments /// public X509Certificate EncryptionCertificate { get; set; } + /// + /// Specifies the failover partner server name and port + /// + public string FailoverPartner { get; set; } = string.Empty; + /// /// Initialization constructor /// From 499b0952061437fd7a10db7913249a7145258a02 Mon Sep 17 00:00:00 2001 From: Apoorv Deshmukh Date: Wed, 5 Nov 2025 15:06:00 +0530 Subject: [PATCH 67/76] [5.1] Use UTF8 instance that doesn't emit BOM (#3617) This commit ports #3399 to release/5.1 --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 9 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 9 +- .../ManualTests/DataCommon/DataTestUtility.cs | 10 + ....Data.SqlClient.ManualTesting.Tests.csproj | 2 + .../SqlBulkCopyTest/TestBulkCopyWithUTF8.cs | 200 ++++++++++++++++++ 5 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs 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 dae197f5f7..01e29ef579 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 @@ -39,6 +39,7 @@ internal struct SNIErrorDetails // and surfacing objects to the user. internal sealed partial class TdsParser { + private static readonly Encoding s_utf8EncodingWithoutBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); private static int _objectTypeCount; // EventSource counter private readonly SqlClientLogger _logger = new SqlClientLogger(); @@ -2767,7 +2768,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, // UTF8 collation if (env._newCollation.IsUTF8) { - _defaultEncoding = Encoding.UTF8; + _defaultEncoding = s_utf8EncodingWithoutBom; } else { @@ -4171,7 +4172,7 @@ internal bool TryProcessReturnValue(int length, TdsParserStateObject stateObj, o // UTF8 collation if (rec.collation.IsUTF8) { - rec.encoding = Encoding.UTF8; + rec.encoding = s_utf8EncodingWithoutBom; } else { @@ -4955,7 +4956,7 @@ private bool TryProcessTypeInfo(TdsParserStateObject stateObj, SqlMetaDataPriv c // UTF8 collation if (col.collation.IsUTF8) { - col.encoding = Encoding.UTF8; + col.encoding = s_utf8EncodingWithoutBom; } else { @@ -10681,7 +10682,7 @@ internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsPars // Replace encoding if it is UTF8 if (metadata.collation.IsUTF8) { - _defaultEncoding = Encoding.UTF8; + _defaultEncoding = s_utf8EncodingWithoutBom; } _defaultCollation = metadata.collation; 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 c94f714973..264e132009 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 @@ -32,6 +32,7 @@ namespace Microsoft.Data.SqlClient // and surfacing objects to the user. sealed internal class TdsParser { + private static readonly Encoding s_utf8EncodingWithoutBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); private static int _objectTypeCount; // EventSource Counter private readonly SqlClientLogger _logger = new SqlClientLogger(); @@ -3221,7 +3222,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, // UTF8 collation if (env._newCollation.IsUTF8) { - _defaultEncoding = Encoding.UTF8; + _defaultEncoding = s_utf8EncodingWithoutBom; } else { @@ -4739,7 +4740,7 @@ internal bool TryProcessReturnValue(int length, if (rec.collation.IsUTF8) { // UTF8 collation - rec.encoding = Encoding.UTF8; + rec.encoding = s_utf8EncodingWithoutBom; } else { @@ -5636,7 +5637,7 @@ private bool TryProcessTypeInfo(TdsParserStateObject stateObj, SqlMetaDataPriv c if (col.collation.IsUTF8) { // UTF8 collation - col.encoding = Encoding.UTF8; + col.encoding = s_utf8EncodingWithoutBom; } else { @@ -11670,7 +11671,7 @@ internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsPars // Replace encoding if it is UTF8 if (metadata.collation.IsUTF8) { - _defaultEncoding = Encoding.UTF8; + _defaultEncoding = s_utf8EncodingWithoutBom; } _defaultCollation = metadata.collation; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 4f842cf142..51acea439c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -593,6 +593,16 @@ public static bool IsSupportingDistributedTransactions() #endif } + public static void CreateTable(SqlConnection sqlConnection, string tableName, string createBody) + { + DropTable(sqlConnection, tableName); + string tableCreate = "CREATE TABLE " + tableName + createBody; + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = tableCreate; + command.ExecuteNonQuery(); + } + } public static void DropTable(SqlConnection sqlConnection, string tableName) { ResurrectConnection(sqlConnection); 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 7b8e551bc9..0c22396401 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 @@ -279,6 +279,8 @@ + + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs new file mode 100644 index 0000000000..75e34dee3d --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs @@ -0,0 +1,200 @@ +// 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.Data; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.SqlBulkCopyTest +{ + /// + /// Validates SqlBulkCopy functionality when working with UTF-8 encoded data. + /// Ensures that data copied from a UTF-8 source table to a destination table retains its encoding and content integrity. + /// + public sealed class TestBulkCopyWithUtf8 : IDisposable + { + private static string s_sourceTable = DataTestUtility.GetShortName("SourceTableForUTF8Data"); + private static string s_destinationTable = DataTestUtility.GetShortName("DestinationTableForUTF8Data"); + private static string s_testValue = "test"; + private static byte[] s_testValueInUtf8Bytes = new byte[] { 0x74, 0x65, 0x73, 0x74 }; + private static readonly string s_insertQuery = $"INSERT INTO {s_sourceTable} VALUES('{s_testValue}')"; + + /// + /// Constructor: Initializes and populates source and destination tables required for the tests. + /// + public TestBulkCopyWithUtf8() + { + using SqlConnection sourceConnection = new SqlConnection(GetConnectionString(true)); + sourceConnection.Open(); + SetupTables(sourceConnection, s_sourceTable, s_destinationTable, s_insertQuery); + } + + /// + /// Cleanup method to drop tables after test completion. + /// + public void Dispose() + { + using SqlConnection connection = new SqlConnection(GetConnectionString(true)); + connection.Open(); + DataTestUtility.DropTable(connection, s_sourceTable); + DataTestUtility.DropTable(connection, s_destinationTable); + connection.Close(); + } + + /// + /// Builds a connection string with or without Multiple Active Result Sets (MARS) property. + /// + private string GetConnectionString(bool enableMars) + { + return new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + { + MultipleActiveResultSets = enableMars + }.ConnectionString; + } + + /// + /// Creates source and destination tables with a varchar(max) column with a collation setting + /// that stores the data in UTF8 encoding and inserts the data in the source table. + /// + private void SetupTables(SqlConnection connection, string sourceTable, string destinationTable, string insertQuery) + { + string columnDefinition = "(str_col varchar(max) COLLATE Latin1_General_100_CS_AS_KS_WS_SC_UTF8)"; + DataTestUtility.CreateTable(connection, sourceTable, columnDefinition); + DataTestUtility.CreateTable(connection, destinationTable, columnDefinition); + using SqlCommand insertCommand = connection.CreateCommand(); + insertCommand.CommandText = insertQuery; + Helpers.TryExecute(insertCommand, insertQuery); + } + + /// + /// Synchronous test case: Validates that data copied using SqlBulkCopy matches UTF-8 byte sequence for test value. + /// Tested with MARS enabled and disabled, and with streaming enabled and disabled. + /// + [ConditionalTheory(typeof(DataTestUtility), + nameof(DataTestUtility.AreConnStringsSetup), + nameof(DataTestUtility.IsNotAzureServer), + nameof(DataTestUtility.IsNotAzureSynapse))] + [InlineData(true, true)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(false, false)] + public void BulkCopy_Utf8Data_ShouldMatchSource(bool isMarsEnabled, bool enableStreaming) + { + // Setup connections for source and destination tables + string connectionString = GetConnectionString(isMarsEnabled); + using SqlConnection sourceConnection = new SqlConnection(connectionString); + sourceConnection.Open(); + using SqlConnection destinationConnection = new SqlConnection(connectionString); + destinationConnection.Open(); + + // Read data from source table + using SqlCommand sourceDataCommand = new SqlCommand($"SELECT str_col FROM {s_sourceTable}", sourceConnection); + using SqlDataReader reader = sourceDataCommand.ExecuteReader(CommandBehavior.SequentialAccess); + + // Verify that the destination table is empty before bulk copy + using SqlCommand countCommand = new SqlCommand($"SELECT COUNT(*) FROM {s_destinationTable}", destinationConnection); + Assert.Equal(0, Convert.ToInt16(countCommand.ExecuteScalar())); + + // Initialize bulk copy configuration + using SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection) + { + EnableStreaming = enableStreaming, + DestinationTableName = s_destinationTable + }; + + try + { + // Perform bulk copy from source to destination table + bulkCopy.WriteToServer(reader); + } + catch (Exception ex) + { + // If bulk copy fails, fail the test with the exception message + Assert.Fail($"Bulk copy failed: {ex.Message}"); + } + + // Verify that the 1 row from the source table has been copied into our destination table. + Assert.Equal(1, Convert.ToInt16(countCommand.ExecuteScalar())); + + // Read the data from destination table as varbinary to verify the UTF-8 byte sequence + using SqlCommand verifyCommand = new SqlCommand($"SELECT cast(str_col as varbinary) FROM {s_destinationTable}", destinationConnection); + using SqlDataReader verifyReader = verifyCommand.ExecuteReader(CommandBehavior.SequentialAccess); + + // Verify that we have data in the destination table + Assert.True(verifyReader.Read(), "No data found in destination table after bulk copy."); + + // Read the value of the column as SqlBinary. + byte[] actualBytes = verifyReader.GetSqlBinary(0).Value; + + // Verify that the byte array matches the expected UTF-8 byte sequence + Assert.Equal(s_testValueInUtf8Bytes.Length, actualBytes.Length); + Assert.Equal(s_testValueInUtf8Bytes, actualBytes); + } + + /// + /// Asynchronous version of the testcase BulkCopy_Utf8Data_ShouldMatchSource + /// + [ConditionalTheory(typeof(DataTestUtility), + nameof(DataTestUtility.AreConnStringsSetup), + nameof(DataTestUtility.IsNotAzureServer), + nameof(DataTestUtility.IsNotAzureSynapse))] + [InlineData(true, true)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(false, false)] + public async Task BulkCopy_Utf8Data_ShouldMatchSource_Async(bool isMarsEnabled, bool enableStreaming) + { + // Setup connections for source and destination tables + string connectionString = GetConnectionString(isMarsEnabled); + using SqlConnection sourceConnection = new SqlConnection(connectionString); + await sourceConnection.OpenAsync(); + using SqlConnection destinationConnection = new SqlConnection(connectionString); + await destinationConnection.OpenAsync(); + + // Read data from source table + using SqlCommand sourceDataCommand = new SqlCommand($"SELECT str_col FROM {s_sourceTable}", sourceConnection); + using SqlDataReader reader = await sourceDataCommand.ExecuteReaderAsync(CommandBehavior.SequentialAccess); + + // Verify that the destination table is empty before bulk copy + using SqlCommand countCommand = new SqlCommand($"SELECT COUNT(*) FROM {s_destinationTable}", destinationConnection); + Assert.Equal(0, Convert.ToInt16(await countCommand.ExecuteScalarAsync())); + + // Initialize bulk copy configuration + using SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection) + { + EnableStreaming = enableStreaming, + DestinationTableName = s_destinationTable + }; + + try + { + // Perform bulk copy from source to destination table + await bulkCopy.WriteToServerAsync(reader); + } + catch (Exception ex) + { + // If bulk copy fails, fail the test with the exception message + Assert.Fail($"Bulk copy failed: {ex.Message}"); + } + + // Verify that the 1 row from the source table has been copied into our destination table. + Assert.Equal(1, Convert.ToInt16(await countCommand.ExecuteScalarAsync())); + + // Read the data from destination table as varbinary to verify the UTF-8 byte sequence + using SqlCommand verifyCommand = new SqlCommand($"SELECT cast(str_col as varbinary) FROM {s_destinationTable}", destinationConnection); + using SqlDataReader verifyReader = await verifyCommand.ExecuteReaderAsync(CommandBehavior.SequentialAccess); + + // Verify that we have data in the destination table + Assert.True(await verifyReader.ReadAsync(), "No data found in destination table after bulk copy."); + + // Read the value of the column as SqlBinary. + byte[] actualBytes = verifyReader.GetSqlBinary(0).Value; + + // Verify that the byte array matches the expected UTF-8 byte sequence + Assert.Equal(s_testValueInUtf8Bytes.Length, actualBytes.Length); + Assert.Equal(s_testValueInUtf8Bytes, actualBytes); + } + } +} From 080c71207d8535a126f5f8953251f529cb9836eb Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 13 Nov 2025 08:21:35 -0400 Subject: [PATCH 68/76] [5.1] Update Dependencies & Fix Build Issues (#3754) --- build.proj | 4 +- .../jobs/build-signed-akv-package-job.yml | 7 +- .../steps/copy-dlls-for-test-step.yml | 10 +- .../dotnet-sqlclient-signing-pipeline.yml | 22 ++- eng/pipelines/libraries/akv-variables.yml | 20 +- eng/pipelines/libraries/build-variables.yml | 2 +- eng/pipelines/libraries/mds-variables.yml | 21 +- src/Directory.Build.props | 12 ++ src/Microsoft.Data.SqlClient.sln | 22 +-- ...waysEncrypted.AzureKeyVaultProvider.csproj | 1 + .../ref/Microsoft.Data.SqlClient.csproj | 8 +- .../src/Microsoft.Data.SqlClient.csproj | 8 +- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 5 + .../netfx/src/Microsoft.Data.SqlClient.csproj | 43 +++-- .../GenerateResourceStringsSource.targets | 5 +- .../ActiveDirectoryAuthenticationProvider.cs | 6 + ...ncryptionCertificateStoreProviderShould.cs | 2 +- .../AssertExtensions.cs | 4 +- .../BaseProviderAsyncTest.cs | 76 ++++---- .../Microsoft.Data.SqlClient.Tests.csproj | 26 ++- .../PlatformDetection.cs | 0 .../SqlBulkCopyColumnMappingCollectionTest.cs | 13 +- ...qlBulkCopyColumnOrderHintCollectionTest.cs | 13 +- .../SqlConfigurableRetryLogicTest.cs | 7 +- .../SqlConnectionBasicTests.cs | 4 +- .../SqlConnectionTest.RetrieveStatistics.cs | 5 - .../FunctionalTests/SqlDataRecordTest.cs | 2 +- .../tests/FunctionalTests/SqlErrorTest.cs | 2 +- .../tests/FunctionalTests/SqlExceptionTest.cs | 2 +- .../tests/FunctionalTests/SqlHelperTest.cs | 2 +- .../AlwaysEncrypted/AKVUnitTests.cs | 2 +- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 2 +- .../AlwaysEncrypted/ConversionTests.cs | 4 +- .../AlwaysEncrypted/End2EndSmokeTests.cs | 2 +- .../TestFixtures/DatabaseHelper.cs | 2 +- .../ManualTests/DataCommon/DataTestUtility.cs | 8 +- ....Data.SqlClient.ManualTesting.Tests.csproj | 59 +++--- .../ManualTests/SQL/AsyncTest/AsyncTest.cs | 2 +- .../SQL/AsyncTest/AsyncTimeoutTest.cs | 2 +- .../SQL/AsyncTest/XmlReaderAsyncTest.cs | 2 +- .../ConnectivityTests/AADConnectionTest.cs | 4 +- .../SQL/ConnectivityTests/ConnectivityTest.cs | 2 +- .../SQL/DataReaderTest/DataReaderTest.cs | 2 +- .../SQL/DataStreamTest/DataStreamTest.cs | 8 +- .../ExceptionTest/ConnectionExceptionTest.cs | 8 +- .../IntegratedAuthenticationTest.cs | 2 +- .../SQL/LocalDBTest/LocalDBTest.cs | 38 ++-- .../MARSSessionPoolingTest.cs | 4 +- .../ManualTests/SQL/MARSTest/MARSTest.cs | 2 +- .../SQL/ParameterTest/ParametersTest.cs | 4 +- .../ManualTests/SQL/ParameterTest/TvpTest.cs | 6 +- .../SQL/RetryLogic/RetryLogicCounterTest.cs | 4 +- .../RetryLogic/SqlCommandReliabilityTest.cs | 4 +- .../SqlConnectionReliabilityTest.cs | 4 +- .../SQL/SplitPacketTest/SplitPacketTest.cs | 2 +- .../SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs | 2 +- .../SQL/SqlCommand/SqlCommandCancelTest.cs | 8 +- .../SqlDependencyTest/SqlDependencyTest.cs | 4 +- .../SqlNotificationTest.cs | 2 +- .../TransactionEnlistmentTest.cs | 2 +- .../SQL/UdtTest/UdtBulkCopyTest.cs | 2 +- .../tests/ManualTests/SQL/UdtTest/UdtTest2.cs | 2 +- .../WeakRefTestYukonSpecific.cs | 2 +- ...crosoft.Data.SqlClient.ExtUtilities.csproj | 1 + ...rosoft.Data.SqlClient.TestUtilities.csproj | 3 +- .../xunit.runner.json | 9 + .../Attributes/ActiveIssueAttribute.cs | 24 --- .../Attributes/ConditionalClassAttribute.cs | 23 --- .../Attributes/ConditionalFactAttribute.cs | 28 --- .../Attributes/ConditionalTheoryAttribute.cs | 28 --- .../Attributes/OuterLoopAttribute.cs | 20 -- .../Attributes/PlatformSpecificAttribute.cs | 19 -- .../Attributes/SkipOnCoreClrAttribute.cs | 21 -- .../Attributes/SkipOnMonoAttribute.cs | 18 -- .../SkipOnTargetFrameworkAttribute.cs | 19 -- .../Attributes/TestCategoryAttribute.cs | 19 -- .../ConditionalDiscovererException.cs | 13 -- .../DesktopTraceListener.cs | 32 ---- .../DiscovererHelpers.cs | 26 --- .../Discoverers/ActiveIssueDiscoverer.cs | 63 ------ .../Discoverers/ConditionalClassDiscoverer.cs | 70 ------- .../Discoverers/ConditionalFactDiscoverer.cs | 27 --- .../Discoverers/ConditionalTestDiscoverer.cs | 180 ------------------ .../ConditionalTheoryDiscoverer.cs | 59 ------ .../Discoverers/OuterLoopTestsDiscoverer.cs | 27 --- .../Discoverers/PlatformSpecificDiscoverer.cs | 39 ---- .../Discoverers/SkipOnCoreClrDiscoverer.cs | 103 ---------- .../Discoverers/SkipOnMonoDiscoverer.cs | 39 ---- .../SkipOnTargetFrameworkDiscoverer.cs | 37 ---- .../Discoverers/TestCategoryDiscoverer.cs | 31 --- .../Extensions/TheoryExtensions.cs | 38 ---- .../Microsoft.DotNet.XUnitExtensions.csproj | 56 ------ .../RuntimeStressTestModes.cs | 39 ---- .../SkippedFactTestCase.cs | 39 ---- .../SkippedTestCase.cs | 48 ----- .../SkippedTestMessageBus.cs | 50 ----- .../SkippedTheoryTestCase.cs | 39 ---- .../TargetFrameworkMonikers.cs | 17 -- .../TestPlatforms.cs | 20 -- .../XunitConstants.cs | 28 --- .../Microsoft.Cci.Extensions.csproj | 2 +- tools/props/Versions.props | 24 ++- tools/specs/Microsoft.Data.SqlClient.nuspec | 68 ++++--- ...waysEncrypted.AzureKeyVaultProvider.nuspec | 12 +- ...DllsForNetFxProjectReferenceBuilds.targets | 46 +++++ tools/targets/NotSupported.targets | 2 +- 106 files changed, 437 insertions(+), 1626 deletions(-) rename src/Microsoft.Data.SqlClient/tests/{tools/Microsoft.DotNet.XUnitExtensions/Extensions => FunctionalTests}/AssertExtensions.cs (98%) rename src/Microsoft.Data.SqlClient/tests/{tools/Microsoft.DotNet.XUnitExtensions/Extensions => FunctionalTests}/PlatformDetection.cs (100%) create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ActiveIssueAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalClassAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalFactAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalTheoryAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/OuterLoopAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/PlatformSpecificAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnCoreClrAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnMonoAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnTargetFrameworkAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/TestCategoryAttribute.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/ConditionalDiscovererException.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/DesktopTraceListener.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/DiscovererHelpers.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ActiveIssueDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalClassDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalFactDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalTestDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalTheoryDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/OuterLoopTestsDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/PlatformSpecificDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnCoreClrDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnMonoDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnTargetFrameworkDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/TestCategoryDiscoverer.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/TheoryExtensions.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Microsoft.DotNet.XUnitExtensions.csproj delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/RuntimeStressTestModes.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedFactTestCase.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTestCase.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTestMessageBus.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTheoryTestCase.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/TargetFrameworkMonikers.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/TestPlatforms.cs delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/XunitConstants.cs create mode 100644 tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets diff --git a/build.proj b/build.proj index 1f295abc94..f510884678 100644 --- a/build.proj +++ b/build.proj @@ -53,7 +53,6 @@ - @@ -64,7 +63,6 @@ - @@ -105,7 +103,7 @@ - $(DotNetCmd) dotnet build -c Release -p:ReferenceType=$(ReferenceType) + $(DotnetPath)dotnet build -c Release -p:ReferenceType=$(ReferenceType) diff --git a/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml b/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml index 43109253dc..8c0d31e5d1 100644 --- a/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml +++ b/eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml @@ -4,6 +4,9 @@ # See the LICENSE file in the project root for more information. # ################################################################################# parameters: + - name: mdsPackageVersion + type: string + - name: symbolsFolder type: string default: symbols @@ -33,14 +36,14 @@ jobs: - template: ../steps/build-all-configurations-signed-dlls-step.yml@self parameters: product: AKV - nugetPackageRefVersion: $(MDS_PackageRef_Version) + nugetPackageRefVersion: ${{ parameters.mdsPackageVersion }} AssemblyFileVersion: $(AKVAssemblyFileVersion) - template: ../steps/code-analyze-step.yml@self parameters: analyzeType: all product: AKV - nugetPackageRefVersion: $(MDS_PackageRef_Version) + nugetPackageRefVersion: ${{ parameters.mdsPackageVersion }} - template: ../steps/esrp-code-signing-step.yml@self parameters: diff --git a/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml b/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml index ec1ac1f94f..63ea0649ac 100644 --- a/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml +++ b/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml @@ -58,7 +58,7 @@ steps: if ($tf.StartsWith('net4')) { - Copy-Item "artifacts\${{parameters.referenceType }}\bin\Windows_NT\${{parameters.Configuration }}.AnyCPU\Microsoft.Data.SqlClient\netfx\Microsoft.Data.SqlClient.dll" "$software\win\$tf" -recurse + Copy-Item "artifacts\${{parameters.referenceType }}\bin\Windows_NT\${{parameters.Configuration }}.AnyCPU\Microsoft.Data.SqlClient\netfx\$tf\Microsoft.Data.SqlClient.dll" "$software\win\$tf" -recurse } else { @@ -70,16 +70,16 @@ steps: if ($tf.StartsWith('net4')) { - Copy-Item "artifacts\Project\bin\Windows_NT\Release.AnyCPU\Microsoft.Data.SqlClient\netfx\Microsoft.Data.SqlClient.pdb" "$symbols\win\$tf" -recurse + Copy-Item "artifacts\${{parameters.referenceType }}\bin\Windows_NT\${{parameters.Configuration }}.AnyCPU\Microsoft.Data.SqlClient\netfx\$tf\Microsoft.Data.SqlClient.pdb" "$symbols\win\$tf" -recurse } else { - Copy-Item "artifacts\Project\bin\Windows_NT\Release.AnyCPU\Microsoft.Data.SqlClient\netcore\$tf\Microsoft.Data.SqlClient.pdb" "$symbols\win\$tf" -recurse + Copy-Item "artifacts\${{parameters.referenceType }}\bin\Windows_NT\${{parameters.Configuration }}.AnyCPU\Microsoft.Data.SqlClient\netcore\$tf\Microsoft.Data.SqlClient.pdb" "$symbols\win\$tf" -recurse } Write-Host "Artifacts fetched for testing" Get-Location - displayName: 'Prepare ${{ targetFramework }} Arifacts for Testing' + displayName: 'Prepare ${{ targetFramework }} Artifacts for Testing' - ${{ if eq(parameters.product, 'AKV') }}: - powershell: | @@ -96,7 +96,7 @@ steps: Write-Host "Artifacts fetched for testing" Get-Location - displayName: 'Prepare ${{ targetFramework }} Arifacts for Testing' + displayName: 'Prepare ${{ targetFramework }} Artifacts for Testing' - powershell: | $software = '${{parameters.softwareFolder}}' diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml index b422fa7836..fe11f02128 100644 --- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml @@ -43,10 +43,13 @@ parameters: # parameters are shown up in ADO UI in a build queue time type: boolean default: false +# GOTCHA: This must be a previously published MDS package version. This +# pipeline isn't capable of using the MDS package being built as input for +# the AKV package build. - name: MDS_PackageRef_Version - displayName: 'MDS package version of AKV Provider (build AKV)' + displayName: 'Publicly available MDS package version for AKV Provider build' type: string - default: 5.1.2 + default: 5.1.7 - name: CurrentNetFxVersion displayName: 'Lowest supported .NET Framework version (MDS validation)' @@ -77,8 +80,6 @@ variables: value: drop_buildMDS_build_signed_package - name: PublishSymbols value: ${{ parameters['publishSymbols'] }} - - name: MDS_PackageRef_Version - value: ${{ parameters['MDS_PackageRef_Version'] }} - name: CurrentNetFxVersion value: ${{ parameters['CurrentNetFxVersion'] }} - name: ProductVersion #MDS product version (MDS validation) @@ -144,22 +145,23 @@ extends: tsaOptionsPath: $(REPOROOT)\.config\tsaoptions.json disableLegacyManifest: true stages: - - stage: buildAKV - displayName: 'Build AKV Provider' + - stage: buildMDS + displayName: 'Build MDS' jobs: - - template: eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml@self + - template: eng/pipelines/common/templates/jobs/build-signed-package-job.yml@self parameters: symbolsFolder: $(symbolsFolder) softwareFolder: $(softwareFolder) publishSymbols: ${{ parameters['publishSymbols'] }} - - stage: buildMDS - displayName: 'Build MDS' + - stage: buildAKV + displayName: 'Build AKV Provider' jobs: - - template: eng/pipelines/common/templates/jobs/build-signed-package-job.yml@self + - template: eng/pipelines/common/templates/jobs/build-signed-akv-package-job.yml@self parameters: symbolsFolder: $(symbolsFolder) softwareFolder: $(softwareFolder) + mdsPackageVersion: ${{ parameters['MDS_PackageRef_Version'] }} publishSymbols: ${{ parameters['publishSymbols'] }} - stage: mds_package_validation diff --git a/eng/pipelines/libraries/akv-variables.yml b/eng/pipelines/libraries/akv-variables.yml index a4aa80d555..eefd829121 100644 --- a/eng/pipelines/libraries/akv-variables.yml +++ b/eng/pipelines/libraries/akv-variables.yml @@ -6,16 +6,26 @@ variables: - group: AKV Release Variables - - name: AKVMajor + + # The above Library contains variables named AKVMajor, AKVMinor, and AKVPatch. + # We don't want to use those (since they are shared with other pipelines), so + # we specify our own variables here. These are the only variables that affect + # the NuGet package version and assembly version for AKV official builds for + # this branch (5.1). + # + # There are other variables in that Library that we still need, so we can't + # omit it entirely. + # + - name: LocalAkvMajor value: 5 - - name: AKVMinor + - name: LocalAkvMinor value: 1 - - name: AKVPatch + - name: LocalAkvPatch value: 0 - name: AKVNugetPackageVersion - value: $(AKVMajor).$(AKVMinor).$(AKVPatch) + value: $(LocalAkvMajor).$(LocalAkvMinor).$(LocalAkvPatch) - name: AKVAssemblyFileVersion - value: '$(AKVMajor).$(AKVMinor)$(AKVPatch).$(Build.BuildNumber)' + value: '$(LocalAkvMajor).$(LocalAkvMinor)$(LocalAkvPatch).$(Build.BuildNumber)' - name: akvNuspecPath value: tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec diff --git a/eng/pipelines/libraries/build-variables.yml b/eng/pipelines/libraries/build-variables.yml index 1c26f26a69..7b3b13f914 100644 --- a/eng/pipelines/libraries/build-variables.yml +++ b/eng/pipelines/libraries/build-variables.yml @@ -6,5 +6,5 @@ variables: - template: common-variables.yml@self - - template: akv-variables.yml@self - template: mds-variables.yml@self + - template: akv-variables.yml@self diff --git a/eng/pipelines/libraries/mds-variables.yml b/eng/pipelines/libraries/mds-variables.yml index 8d5e37ab83..e32c298ae8 100644 --- a/eng/pipelines/libraries/mds-variables.yml +++ b/eng/pipelines/libraries/mds-variables.yml @@ -7,18 +7,27 @@ variables: - group: Release Variables - - name: Major + # The above Library contains variables named Major, Minor, and Patch. We + # don't want to use those (since they are shared with other pipelines), so we + # specify our own variables here. These are the only variables that affect + # the NuGet package version and assembly version for MDS official builds for + # this branch (5.1). + # + # There are other variables in that Library that we still need, so we can't + # omit it entirely. + # + - name: LocalMdsMajor value: 5 - - name: Minor + - name: LocalMdsMinor value: 1 - - name: Patch - value: 7 + - name: LocalMdsPatch + value: 8 - name: Packaging.EnableSBOMSigning value: true - name: NugetPackageVersion - value: $(Major).$(Minor).$(Patch) + value: $(LocalMdsMajor).$(LocalMdsMinor).$(LocalMdsPatch) - name: AssemblyFileVersion - value: '$(Major).$(Minor)$(Patch).$(Build.BuildNumber)' + value: '$(LocalMdsMajor).$(LocalMdsMinor)$(LocalMdsPatch).$(Build.BuildNumber)' - name: nuspecPath value: '$(REPOROOT)/tools/specs/Microsoft.Data.SqlClient.nuspec' diff --git a/src/Directory.Build.props b/src/Directory.Build.props index be2fe1f22a..d99e265379 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -105,4 +105,16 @@ + + + false + + + + + $(NoWarn);NETSDK1138 + diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index c5b34945a0..b81edeb8e8 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -1,3 +1,4 @@ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31912.275 @@ -11,9 +12,6 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TDS.Servers", "Microsoft.Data.SqlClient\tests\tools\TDS\TDS.Servers\TDS.Servers.csproj", "{978063D3-FBB5-4E10-8C45-67C90BE1B931}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TDS", "Microsoft.Data.SqlClient\tests\tools\TDS\TDS\TDS.csproj", "{8DC9D1A0-351B-47BC-A90F-B9DA542550E9}" - ProjectSection(ProjectDependencies) = postProject - {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {FDA6971D-9F57-4DA4-B10A-261C91684CFC} - EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.Tests", "Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj", "{D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}" ProjectSection(ProjectDependencies) = postProject @@ -28,7 +26,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "netfx", "netfx", "{3FDD425C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Address", "Microsoft.Data.SqlClient\tests\ManualTests\SQL\UdtTest\UDTs\Address\Address.csproj", "{D1392B54-998A-4F27-BC17-4CE149117BCC}" ProjectSection(ProjectDependencies) = postProject - {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {FDA6971D-9F57-4DA4-B10A-261C91684CFC} {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} {407890AC-9876-4FEF-A6F1-F36A876BAADE} = {407890AC-9876-4FEF-A6F1-F36A876BAADE} EndProjectSection @@ -40,13 +37,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.Ma EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Circle", "Microsoft.Data.SqlClient\tests\ManualTests\SQL\UdtTest\UDTs\Circle\Circle.csproj", "{6C88F00F-9597-43AD-9E5F-9B344DA3B16F}" ProjectSection(ProjectDependencies) = postProject - {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {FDA6971D-9F57-4DA4-B10A-261C91684CFC} {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shapes", "Microsoft.Data.SqlClient\tests\ManualTests\SQL\UdtTest\UDTs\Shapes\Shapes.csproj", "{B73A7063-37C3-415D-AD53-BB3DA20ABD6E}" ProjectSection(ProjectDependencies) = postProject - {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {FDA6971D-9F57-4DA4-B10A-261C91684CFC} {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} EndProjectSection EndProject @@ -72,8 +67,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.Al EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "add-ons", "add-ons", "{C9726AED-D6A3-4AAC-BA04-92DD1F079594}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.XUnitExtensions", "Microsoft.Data.SqlClient\tests\tools\Microsoft.DotNet.XUnitExtensions\Microsoft.DotNet.XUnitExtensions.csproj", "{FDA6971D-9F57-4DA4-B10A-261C91684CFC}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{ED952CF7-84DF-437A-B066-F516E9BE1C2C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "snippets", "snippets", "{71F356DC-DFA3-4163-8BFE-D268722CE189}" @@ -383,18 +376,6 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x64.Build.0 = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x86.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x86.Build.0 = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|x64.ActiveCfg = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|x64.Build.0 = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|x86.ActiveCfg = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|x86.Build.0 = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|Any CPU.Build.0 = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x64.ActiveCfg = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x64.Build.0 = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x86.ActiveCfg = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x86.Build.0 = Release|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x64.ActiveCfg = Debug|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x64.Build.0 = Debug|Any CPU @@ -510,7 +491,6 @@ Global {771F3F1E-7A68-4A9D-ADA8-A24F1D5BE71D} = {3FDD425C-FE01-4B56-863E-1FCDD0677CF5} {412BCCC8-19F6-489A-B594-E9A506816155} = {771F3F1E-7A68-4A9D-ADA8-A24F1D5BE71D} {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7} = {C9726AED-D6A3-4AAC-BA04-92DD1F079594} - {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {0CC4817A-12F3-4357-912C-09315FAAD008} {71F356DC-DFA3-4163-8BFE-D268722CE189} = {ED952CF7-84DF-437A-B066-F516E9BE1C2C} {908C7DD3-C999-40A6-9433-9F5ACA7C36F5} = {71F356DC-DFA3-4163-8BFE-D268722CE189} {0CE216CE-8072-4985-B248-61F0D0BE9C2E} = {71F356DC-DFA3-4163-8BFE-D268722CE189} 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 ca570955ca..d68e3ee661 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 @@ -31,6 +31,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index 912d68f5de..7ef9ff4f78 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -35,9 +35,6 @@ - - - @@ -58,6 +55,11 @@ + + 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 f06bc9df7b..77b2470387 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -948,9 +948,6 @@ - - - @@ -972,6 +969,11 @@ + + diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index ba22087d50..ec678e21f7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -38,6 +38,11 @@ + + 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 cd49858ff6..bceff2fc4c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -1,10 +1,11 @@  - - + {407890AC-9876-4FEF-A6F1-F36A876BAADE} SqlClient - v4.6.2 + net462 + false + false true Strings SqlClient.Resources.$(ResxFileName) @@ -17,9 +18,6 @@ $(OutputPath)\Microsoft.Data.SqlClient.xml $(ObjPath)$(AssemblyName)\netfx\ Framework $(BaseProduct) - - True $(DefineConstants);NETFRAMEWORK; @@ -30,7 +28,7 @@ True True False - 1701,1702 + $(NoWarn);1701,1702 True True 4 @@ -43,7 +41,10 @@ True True None - MinimumRecommendedRules.ruleset + + + MinimumRecommendedRules.ruleset + True True $(DefineConstants);USEOFFSET;CODE_ANALYSIS_BASELINE;FEATURE_LEGACYSURFACEAREA;FEATURE_UTF32;FEATURE_UTF7;TRACE; @@ -704,17 +705,6 @@ PreserveNewest - - - {5477469E-83B1-11D2-8B49-00A0C9B7C9C4} - 2 - 4 - 0 - tlbimp - False - True - - $(MicrosoftDataSqlClientSniVersion) @@ -723,6 +713,11 @@ + + @@ -731,7 +726,15 @@ - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/src/Microsoft.Data.SqlClient/netfx/tools/targets/GenerateResourceStringsSource.targets b/src/Microsoft.Data.SqlClient/netfx/tools/targets/GenerateResourceStringsSource.targets index 618fbd6d51..eb2be2ae9d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/tools/targets/GenerateResourceStringsSource.targets +++ b/src/Microsoft.Data.SqlClient/netfx/tools/targets/GenerateResourceStringsSource.targets @@ -1,11 +1,10 @@ - + - \ No newline at end of file + 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 d69cbe06ab..de5732c3aa 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -220,7 +220,9 @@ public override async Task AcquireTokenAsync(SqlAuthenti { if (!string.IsNullOrEmpty(parameters.UserId)) { + #pragma warning disable CS0618 // Type or member is obsolete result = await app.AcquireTokenByIntegratedWindowsAuth(scopes) + #pragma warning restore CS0618 // Type or member is obsolete .WithCorrelationId(parameters.ConnectionId) .WithUsername(parameters.UserId) .ExecuteAsync(cancellationToken: cts.Token) @@ -228,7 +230,9 @@ public override async Task AcquireTokenAsync(SqlAuthenti } else { + #pragma warning disable CS0618 // Type or member is obsolete result = await app.AcquireTokenByIntegratedWindowsAuth(scopes) + #pragma warning restore CS0618 // Type or member is obsolete .WithCorrelationId(parameters.ConnectionId) .ExecuteAsync(cancellationToken: cts.Token) .ConfigureAwait(false); @@ -252,7 +256,9 @@ previousPw is byte[] previousPwBytes && if (null == result) { + #pragma warning disable CS0618 // Type or member is obsolete result = await app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password) + #pragma warning restore CS0618 // Type or member is obsolete .WithCorrelationId(parameters.ConnectionId) .ExecuteAsync(cancellationToken: cts.Token) .ConfigureAwait(false); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs index 5b7dd192ef..31c3504d84 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs @@ -303,7 +303,7 @@ public void TestAeadEncryptionReversal(string dataType, object data, Utility.CCo break; default: - Assert.True(false, "unexpected data type."); + Assert.Fail("unexpected data type."); break; } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/AssertExtensions.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AssertExtensions.cs similarity index 98% rename from src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/AssertExtensions.cs rename to src/Microsoft.Data.SqlClient/tests/FunctionalTests/AssertExtensions.cs index ed0c0af5cf..937a2ad9e0 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/AssertExtensions.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AssertExtensions.cs @@ -310,11 +310,11 @@ public static void Equal(byte[] expected, byte[] actual) { Assert.Equal(expected, actual); } - catch (AssertActualExpectedException) + catch (EqualException) { string expectedString = string.Join(", ", expected); string actualString = string.Join(", ", actual); - throw new AssertActualExpectedException(expectedString, actualString, null); + throw EqualException.ForMismatchedStrings(expectedString, actualString, 0, 0); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/BaseProviderAsyncTest/BaseProviderAsyncTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/BaseProviderAsyncTest/BaseProviderAsyncTest.cs index 0d13ad5f77..d4019c0d10 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/BaseProviderAsyncTest/BaseProviderAsyncTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/BaseProviderAsyncTest/BaseProviderAsyncTest.cs @@ -19,21 +19,21 @@ private static void AssertTaskFaults(Task t) } [Fact] - public static void TestDbConnection() + public static async Task TestDbConnection() { MockConnection connection = new MockConnection(); CancellationTokenSource source = new CancellationTokenSource(); // ensure OpenAsync() calls OpenAsync(CancellationToken.None) AssertEqualsWithDescription(ConnectionState.Closed, connection.State, "Connection state should have been marked as Closed"); - connection.OpenAsync().Wait(); + await connection.OpenAsync(); Assert.False(connection.CancellationToken.CanBeCanceled, "Default cancellation token should not be cancellable"); AssertEqualsWithDescription(ConnectionState.Open, connection.State, "Connection state should have been marked as Open"); connection.Close(); // Verify cancellationToken over-ride AssertEqualsWithDescription(ConnectionState.Closed, connection.State, "Connection state should have been marked as Closed"); - connection.OpenAsync(source.Token).Wait(); + await connection.OpenAsync(source.Token); AssertEqualsWithDescription(ConnectionState.Open, connection.State, "Connection state should have been marked as Open"); connection.Close(); @@ -48,12 +48,12 @@ public static void TestDbConnection() // Verify base implementation does not call Open when passed an already cancelled cancellation token source.Cancel(); AssertEqualsWithDescription(ConnectionState.Closed, connection.State, "Connection state should have been marked as Closed"); - connection.OpenAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); + await connection.OpenAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled); AssertEqualsWithDescription(ConnectionState.Closed, connection.State, "Connection state should have been marked as Closed"); } [Fact] - public static void TestDbCommand() + public static async Task TestDbCommand() { MockCommand command = new MockCommand() { @@ -63,30 +63,30 @@ public static void TestDbCommand() CancellationTokenSource source = new CancellationTokenSource(); // Verify parameter routing and correct synchronous implementation is called - command.ExecuteNonQueryAsync().Wait(); + await command.ExecuteNonQueryAsync(); Assert.False(command.CancellationToken.CanBeCanceled, "Default cancellation token should not be cancellable"); AssertEqualsWithDescription("ExecuteNonQuery", command.LastCommand, "Last command was not as expected"); - command.ExecuteReaderAsync().Wait(); + await command.ExecuteReaderAsync(); AssertEqualsWithDescription(CommandBehavior.Default, command.CommandBehavior, "Command behavior should have been marked as Default"); Assert.False(command.CancellationToken.CanBeCanceled, "Default cancellation token should not be cancellable"); AssertEqualsWithDescription("ExecuteReader", command.LastCommand, "Last command was not as expected"); - command.ExecuteScalarAsync().Wait(); + await command.ExecuteScalarAsync(); Assert.False(command.CancellationToken.CanBeCanceled, "Default cancellation token should not be cancellable"); AssertEqualsWithDescription("ExecuteScalar", command.LastCommand, "Last command was not as expected"); - command.ExecuteNonQueryAsync(source.Token).Wait(); + await command.ExecuteNonQueryAsync(source.Token); AssertEqualsWithDescription("ExecuteNonQuery", command.LastCommand, "Last command was not as expected"); - command.ExecuteReaderAsync(source.Token).Wait(); + await command.ExecuteReaderAsync(source.Token); AssertEqualsWithDescription("ExecuteReader", command.LastCommand, "Last command was not as expected"); - command.ExecuteScalarAsync(source.Token).Wait(); + await command.ExecuteScalarAsync(source.Token); AssertEqualsWithDescription("ExecuteScalar", command.LastCommand, "Last command was not as expected"); - command.ExecuteReaderAsync(CommandBehavior.SequentialAccess).Wait(); + await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess); AssertEqualsWithDescription(CommandBehavior.SequentialAccess, command.CommandBehavior, "Command behavior should have been marked as SequentialAccess"); Assert.False(command.CancellationToken.CanBeCanceled, "Default cancellation token should not be cancellable"); AssertEqualsWithDescription("ExecuteReader", command.LastCommand, "Last command was not as expected"); - command.ExecuteReaderAsync(CommandBehavior.SingleRow, source.Token).Wait(); + await command.ExecuteReaderAsync(CommandBehavior.SingleRow, source.Token); AssertEqualsWithDescription(CommandBehavior.SingleRow, command.CommandBehavior, "Command behavior should have been marked as SingleRow"); AssertEqualsWithDescription("ExecuteReader", command.LastCommand, "Last command was not as expected"); @@ -107,56 +107,55 @@ public static void TestDbCommand() // Verify base implementation does not call Open when passed an already cancelled cancellation token source.Cancel(); command.LastCommand = "Nothing"; - command.ExecuteNonQueryAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); + await command.ExecuteNonQueryAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled); AssertEqualsWithDescription("Nothing", command.LastCommand, "Expected last command to be 'Nothing'"); - command.ExecuteReaderAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); + await command.ExecuteReaderAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled); AssertEqualsWithDescription("Nothing", command.LastCommand, "Expected last command to be 'Nothing'"); - command.ExecuteReaderAsync(CommandBehavior.SequentialAccess, source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); + await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess, source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled); AssertEqualsWithDescription("Nothing", command.LastCommand, "Expected last command to be 'Nothing'"); - command.ExecuteScalarAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); + await command.ExecuteScalarAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled); AssertEqualsWithDescription("Nothing", command.LastCommand, "Expected last command to be 'Nothing'"); // Verify cancellation command.WaitForCancel = true; source = new CancellationTokenSource(); - Task.Factory.StartNew(() => { command.WaitForWaitingForCancel(); source.Cancel(); }); + var task = Task.Factory.StartNew(() => { command.WaitForWaitingForCancel(); source.Cancel(); }); Task result = command.ExecuteNonQueryAsync(source.Token); Assert.True(result.Exception != null, "Task result should be faulted"); + await task; source = new CancellationTokenSource(); - Task.Factory.StartNew(() => { command.WaitForWaitingForCancel(); source.Cancel(); }); + task = Task.Factory.StartNew(() => { command.WaitForWaitingForCancel(); source.Cancel(); }); result = command.ExecuteReaderAsync(source.Token); Assert.True(result.Exception != null, "Task result should be faulted"); + await task; source = new CancellationTokenSource(); - Task.Factory.StartNew(() => { command.WaitForWaitingForCancel(); source.Cancel(); }); + task = Task.Factory.StartNew(() => { command.WaitForWaitingForCancel(); source.Cancel(); }); result = command.ExecuteScalarAsync(source.Token); Assert.True(result.Exception != null, "Task result should be faulted"); + await task; } [Fact] - public static void TestDbDataReader() + public static async Task TestDbDataReader() { var query = Enumerable.Range(1, 2).Select((x) => new object[] { x, x.ToString(), DBNull.Value }); MockDataReader reader = new MockDataReader { Results = query.GetEnumerator() }; CancellationTokenSource source = new CancellationTokenSource(); - Task result; - - result = reader.ReadAsync(); - result.Wait(); + var result = await reader.ReadAsync(); AssertEqualsWithDescription("Read", reader.LastCommand, "Last command was not as expected"); - Assert.True(result.Result, "Should have received a Result from the ReadAsync"); + Assert.True(result, "Should have received a Result from the ReadAsync"); Assert.False(reader.CancellationToken.CanBeCanceled, "Default cancellation token should not be cancellable"); GetFieldValueAsync(reader, 0, 1); Assert.False(reader.CancellationToken.CanBeCanceled, "Default cancellation token should not be cancellable"); GetFieldValueAsync(reader, source.Token, 1, "1"); - result = reader.ReadAsync(source.Token); - result.Wait(); + result = await reader.ReadAsync(source.Token); AssertEqualsWithDescription("Read", reader.LastCommand, "Last command was not as expected"); - Assert.True(result.Result, "Should have received a Result from the ReadAsync"); + Assert.True(result, "Should have received a Result from the ReadAsync"); GetFieldValueAsync(reader, 2, DBNull.Value); GetFieldValueAsync(reader, 2, DBNull.Value); @@ -165,18 +164,17 @@ public static void TestDbDataReader() AssertTaskFaults(reader.GetFieldValueAsync(2)); AssertEqualsWithDescription("GetValue", reader.LastCommand, "Last command was not as expected"); - result = reader.ReadAsync(); - result.Wait(); + result = await reader.ReadAsync(); AssertEqualsWithDescription("Read", reader.LastCommand, "Last command was not as expected"); - Assert.False(result.Result, "Should NOT have received a Result from the ReadAsync"); + Assert.False(result, "Should NOT have received a Result from the ReadAsync"); - result = reader.NextResultAsync(); + result = await reader.NextResultAsync(); AssertEqualsWithDescription("NextResult", reader.LastCommand, "Last command was not as expected"); - Assert.False(result.Result, "Should NOT have received a Result from NextResultAsync"); + Assert.False(result, "Should NOT have received a Result from NextResultAsync"); Assert.False(reader.CancellationToken.CanBeCanceled, "Default cancellation token should not be cancellable"); - result = reader.NextResultAsync(source.Token); + result = await reader.NextResultAsync(source.Token); AssertEqualsWithDescription("NextResult", reader.LastCommand, "Last command was not as expected"); - Assert.False(result.Result, "Should NOT have received a Result from NextResultAsync"); + Assert.False(result, "Should NOT have received a Result from NextResultAsync"); MockDataReader readerFail = new MockDataReader { Results = query.GetEnumerator(), Fail = true }; AssertTaskFaults(readerFail.ReadAsync()); @@ -188,11 +186,11 @@ public static void TestDbDataReader() source.Cancel(); reader.LastCommand = "Nothing"; - reader.ReadAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); + await reader.ReadAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled); AssertEqualsWithDescription("Nothing", reader.LastCommand, "Expected last command to be 'Nothing'"); - reader.NextResultAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); + await reader.NextResultAsync(source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled); AssertEqualsWithDescription("Nothing", reader.LastCommand, "Expected last command to be 'Nothing'"); - reader.GetFieldValueAsync(0, source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled).Wait(); + await reader.GetFieldValueAsync(0, source.Token).ContinueWith((t) => { }, TaskContinuationOptions.OnlyOnCanceled); AssertEqualsWithDescription("Nothing", reader.LastCommand, "Expected last command to be 'Nothing'"); } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index fcc368dc8a..2336768391 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -64,13 +64,32 @@ + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + PreserveNewest + xunit.runner.json + + + @@ -91,9 +110,6 @@ TDS - - Microsoft.DotNet.XUnitExtensions - @@ -105,7 +121,9 @@ - + + + diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/PlatformDetection.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/PlatformDetection.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/PlatformDetection.cs rename to src/Microsoft.Data.SqlClient/tests/FunctionalTests/PlatformDetection.cs diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnMappingCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnMappingCollectionTest.cs index 3ac3d8a279..109268871d 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnMappingCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnMappingCollectionTest.cs @@ -420,7 +420,7 @@ public void InsertAndClear_BehavesAsExpected() Assert.Same(item3, list[2]); list.Clear(); - Assert.Equal(0, list.Count); + Assert.Empty(list); list.Add(item1); list.Add(item3); @@ -435,7 +435,7 @@ public void InsertAndClear_BehavesAsExpected() Assert.Same(item3, list[2]); list.Clear(); - Assert.Equal(0, list.Count); + Assert.Empty(list); } [Fact] @@ -463,12 +463,11 @@ public void Remove_BehavesAsExpected() IList list = CreateCollection(item1, item2); list.Remove(item1); - Assert.Equal(1, list.Count); + Assert.Single(list); Assert.Same(item2, list[0]); list.Remove(item2); - Assert.Equal(0, list.Count); - + Assert.Empty(list); AssertExtensions.Throws(null, () => list.Remove(item2)); AssertExtensions.Throws(null, () => list.Remove(new SqlBulkCopyColumnMapping(2, 2))); AssertExtensions.Throws(null, () => list.Remove("bogus")); @@ -504,11 +503,11 @@ public void RemoveAt_BehavesAsExpected() Assert.Same(item3, list[1]); list.RemoveAt(1); - Assert.Equal(1, list.Count); + Assert.Single(list); Assert.Same(item2, list[0]); list.RemoveAt(0); - Assert.Equal(0, list.Count); + Assert.Empty(list); } [Fact] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index b6104335dd..9cd30eb3f9 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -395,7 +395,7 @@ public void InsertAndClear_BehavesAsExpected() Assert.Same(item3, list[2]); list.Clear(); - Assert.Equal(0, list.Count); + Assert.Empty(list); list.Add(item1); list.Add(item3); @@ -410,7 +410,7 @@ public void InsertAndClear_BehavesAsExpected() Assert.Same(item3, list[2]); list.Clear(); - Assert.Equal(0, list.Count); + Assert.Empty(list); } [Fact] @@ -434,12 +434,11 @@ public void Remove_BehavesAsExpected() IList list = CreateCollection(item1, item2); list.Remove(item1); - Assert.Equal(1, list.Count); + Assert.Single(list); Assert.Same(item2, list[0]); list.Remove(item2); - Assert.Equal(0, list.Count); - + Assert.Empty(list); AssertExtensions.Throws(null, () => list.Remove(item2)); AssertExtensions.Throws(null, () => list.Remove(new SqlBulkCopyColumnOrderHint("column4", SortOrder.Ascending))); AssertExtensions.Throws(null, () => list.Remove("bogus")); @@ -473,11 +472,11 @@ public void RemoveAt_BehavesAsExpected() Assert.Same(item2, list[0]); Assert.Same(item3, list[1]); list.RemoveAt(1); - Assert.Equal(1, list.Count); + Assert.Single(list); Assert.Same(item2, list[0]); list.RemoveAt(0); - Assert.Equal(0, list.Count); + Assert.Empty(list); } [Fact] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConfigurableRetryLogicTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConfigurableRetryLogicTest.cs index f43162cab8..b04115b191 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConfigurableRetryLogicTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConfigurableRetryLogicTest.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Threading.Tasks; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -10,7 +11,7 @@ namespace Microsoft.Data.SqlClient.Tests public class SqlConfigurableRetryLogicTest { [Fact] - public void InvalidExecute() + public async Task InvalidExecute() { SqlRetryLogicOption option = new SqlRetryLogicOption() { @@ -23,8 +24,8 @@ public void InvalidExecute() SqlRetryLogicBaseProvider retryLogicProvider = SqlConfigurableRetryFactory.CreateFixedRetryProvider(option); Assert.Throws(() => retryLogicProvider.Execute(null, null)); - Assert.ThrowsAsync(() => retryLogicProvider.ExecuteAsync(null, null)); - Assert.ThrowsAsync(() => retryLogicProvider.ExecuteAsync(null, null)); + await Assert.ThrowsAsync(() => retryLogicProvider.ExecuteAsync(null, null)); + await Assert.ThrowsAsync(() => retryLogicProvider.ExecuteAsync(null, null)); } [Fact] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index accb6f2201..21bf1d2d16 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -37,7 +37,7 @@ public void ConnectionTest() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] - [ActiveIssue(4830, TestPlatforms.AnyUnix)] + [ActiveIssue("4830", TestPlatforms.AnyUnix)] [PlatformSpecific(TestPlatforms.Windows)] public void IntegratedAuthConnectionTest() { @@ -94,7 +94,7 @@ public void TransientFaultTest(uint errorCode) { Assert.Equal(ConnectionState.Closed, connection.State); } - Assert.False(true, e.Message); + Assert.Fail(e.Message); } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.RetrieveStatistics.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.RetrieveStatistics.cs index 6e08b3e91d..d4fff6473c 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.RetrieveStatistics.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.RetrieveStatistics.cs @@ -181,13 +181,8 @@ public void RetrieveStatistics_Clear_Success() d.Clear(); Assert.Empty(d); - Assert.Equal(0, d.Count); - Assert.Empty(d.Keys); - Assert.Equal(0, d.Keys.Count); - Assert.Empty(d.Values); - Assert.Equal(0, d.Values.Count); } [Fact] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs index 77546de525..7808202931 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs @@ -70,7 +70,7 @@ public void SqlRecordFillTest() char[] expectedValue = new char[] { 'a', 'b', 'e', 'f', 'g' }; Assert.Equal(expectedValue.Length, record.GetChars(3, 0, cb2, 0, 5)); - Assert.Equal(expectedValue, new string(cb2, 0, (int)record.GetChars(3, 0, null, 0, 0))); + Assert.Equal(expectedValue, new string(cb2, 0, (int)record.GetChars(3, 0, null, 0, 0)).ToCharArray()); record.SetString(3, ""); string xyz = "xyz"; diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs index 13b78449f4..c8264eb446 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs @@ -33,7 +33,7 @@ public static void SqlErrorSerializationTest() } catch (Exception ex) { - Assert.False(true, $"Unexpected Exception occurred: {ex.Message}"); + Assert.Fail($"Unexpected Exception occurred: {ex.Message}"); } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlExceptionTest.cs index 71975db7a5..204ab379c1 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlExceptionTest.cs @@ -50,7 +50,7 @@ public static void SqlExcpetionSerializationTest() } catch (Exception ex) { - Assert.False(true, $"Unexpected Exception occurred: {ex.Message}"); + Assert.Fail($"Unexpected Exception occurred: {ex.Message}"); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlHelperTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlHelperTest.cs index 087e5b5c57..44286b8c0e 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlHelperTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlHelperTest.cs @@ -50,7 +50,7 @@ void handleUnobservedException(object o, UnobservedTaskExceptionEventArgs a) if (unobservedExceptionHappend) //Save doing string interpolation in the happy case { var e = UnwrapException(unhandledException); - Assert.False(true, $"Did not expect an unobserved exception, but found a {e?.GetType()} with message \"{e?.Message}\""); + Assert.Fail($"Did not expect an unobserved exception, but found a {e?.GetType()} with message \"{e?.Message}\""); } } finally diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs index ed7061cf65..2111c7a1af 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/AKVUnitTests.cs @@ -64,7 +64,7 @@ private static void ValidateAKVTraces(List eventData, Gui } break; default: - Assert.False(true, "Unexpected event occurred: " + item.Message); + Assert.Fail("Unexpected event occurred: " + item.Message); break; } }); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index b987bdbc99..3c487b532c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -2918,7 +2918,7 @@ private void EndExecuteReaderAsyncCallBack(IAsyncResult asyncResult) catch (Exception e) { testAsyncCallBackStateObject.Completion.SetException(e); - Assert.True(false, $"{e.Message}"); + Assert.Fail($"{e.Message}"); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs index a7be11add4..726bd5d82f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs @@ -525,7 +525,7 @@ List GenerateOutOfRangeValuesForType(SqlDbType type, int length list.Add(new ValueErrorTuple(Single.Epsilon, false)); break; default: - Assert.True(false, "We should never get here"); + Assert.Fail("We should never get here"); break; } @@ -563,7 +563,7 @@ private void ExecuteAndCheckForError(SqlCommand sqlCmd, bool expectError) try { sqlCmd.ExecuteNonQuery(); - Assert.True(false, "We should have gotten an error but passed instead."); + Assert.Fail("We should have gotten an error but passed instead."); } catch (Exception e) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs index ea4b0171eb..874c225d9f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs @@ -106,7 +106,7 @@ public void TestSelectOnEncryptedNonEncryptedColumnsWithEncryptedParameters(stri break; default: - Assert.True(false, "FAILED: No other data type is supported."); + Assert.Fail("FAILED: No other data type is supported."); break; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index 86fa2c38c5..459573606e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -186,7 +186,7 @@ public static void CompareResults(SqlDataReader sqlDataReader, string[] paramete break; #endif default: - Assert.True(false, "FAILED: unexpected data type."); + Assert.Fail("FAILED: unexpected data type."); break; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 51acea439c..212655d373 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -224,7 +224,9 @@ private static Task AcquireTokenAsync(string authorityURL, string userID SecureString securePassword = new SecureString(); securePassword.MakeReadOnly(); + #pragma warning disable CS0618 // Type or member is obsolete result = app.AcquireTokenByUsernamePassword(scopes, userID, password).ExecuteAsync().Result; + #pragma warning restore CS0618 // Type or member is obsolete return result.AccessToken; }); @@ -787,7 +789,7 @@ public static TException ExpectFailure(Action actionThatFails, strin try { actionThatFails(); - Assert.False(true, "ERROR: Did not get expected exception"); + Assert.Fail("ERROR: Did not get expected exception"); return null; } catch (Exception ex) @@ -808,7 +810,7 @@ public static TException ExpectFailure(Action actio try { actionThatFails(); - Assert.False(true, "ERROR: Did not get expected exception"); + Assert.Fail("ERROR: Did not get expected exception"); return null; } catch (Exception ex) @@ -829,7 +831,7 @@ public static TException ExpectFailure - - @@ -138,32 +136,35 @@ + - + + + + + - - - - - - - - - + + - - + + + + + + + @@ -279,8 +280,6 @@ - - @@ -288,6 +287,22 @@ Common\System\Collections\DictionaryExtensions.cs + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + PreserveNewest + xunit.runner.json + + Address @@ -301,9 +316,6 @@ Utf8String - - Microsoft.DotNet.XUnitExtensions - @@ -325,10 +337,11 @@ - + - - - + + + + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTest.cs index 080a66b394..8208f7e496 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTest.cs @@ -10,7 +10,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class AsyncTest { - [ActiveIssue(5533)] // Async Operations slower than Sync operations + [ActiveIssue("5533")] // Async Operations slower than Sync operations [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void TestReadAsyncTimeConsumed() { 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 b516fa1a11..14abfea409 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs @@ -174,7 +174,7 @@ private static async Task QueryAndValidate(AsyncAPI api, int index, string delay } catch (Exception ex) { - Assert.False(true, "Exception occurred: " + ex.Message); + Assert.Fail("Exception occurred: " + ex.Message); } } break; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs index 9d425b1b75..af5045e6ad 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs @@ -89,7 +89,7 @@ public static async void MoveToContentAsyncTest() } catch (Exception ex) { - Assert.False(true, "Exception occurred: " + ex.Message); + Assert.Fail("Exception occurred: " + ex.Message); } } } 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 8a50cb8ae1..15f90bbc91 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -38,7 +38,9 @@ public override async Task AcquireTokenAsync(SqlAuthenti string[] scopes = new string[] { scope }; SecureString password = new SecureString(); + #pragma warning disable CS0618 // Type or member is obsolete AuthenticationResult result = await PublicClientApplicationBuilder.Create(_appClientId) + #pragma warning restore CS0618 // Type or member is obsolete .WithAuthority(parameters.Authority) .Build().AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password) .WithCorrelationId(parameters.ConnectionId) @@ -575,7 +577,7 @@ public static void ADInteractiveUsingSSPI() // Test passes locally everytime, but in pieplines fails randomly with uncertainity. // e.g. Second AAD connection too slow (802ms)! (More than 30% of the first (576ms).) - [ActiveIssue(16058)] + [ActiveIssue("16058")] [ConditionalFact(nameof(IsAADConnStringsSetup))] public static void ConnectionSpeed() { 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 57d6725ce5..9a974ea341 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -85,7 +85,7 @@ public static void EnvironmentHostNameSPIDTest() // Confirm Server Process Id stays the same after query execution Assert.Equal(sessionSpid, sqlConnection.ServerProcessId); } - Assert.True(false, "No non-empty hostname found for the application"); + Assert.Fail("No non-empty hostname found for the application"); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] 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 0aaaaf890d..0eb1a95935 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -159,7 +159,7 @@ public static void CollatedDataReaderTest() } catch (SqlException e) { - Assert.True(false, $"Unexpected Exception occurred: {e.Message}"); + Assert.Fail($"Unexpected Exception occurred: {e.Message}"); } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs index e622121cbb..5bc0c8a7d7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs @@ -31,7 +31,7 @@ public static void RunAllTestsForSingleServer_NP() } } - [ActiveIssue(5540)] + [ActiveIssue("5540")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void RunAllTestsForSingleServer_TCP() { @@ -106,17 +106,17 @@ static async Task LocalCopyTo(Stream source, Stream destination, int bufferSize, int sharedLength = Math.Min(inputData.Length, outputData.Length); if (sharedLength < outputData.Length) { - Assert.False(true, $"output is longer than input, input={inputData.Length} bytes, output={outputData.Length} bytes"); + Assert.Fail($"output is longer than input, input={inputData.Length} bytes, output={outputData.Length} bytes"); } if (sharedLength < inputData.Length) { - Assert.False(true, $"input is longer than output, input={inputData.Length} bytes, output={outputData.Length} bytes"); + Assert.Fail($"input is longer than output, input={inputData.Length} bytes, output={outputData.Length} bytes"); } for (int index = 0; index < sharedLength; index++) { if (inputData[index] != outputData[index]) // avoid formatting the output string unless there is a difference { - Assert.True(false, $"input and output differ at index {index}, input={inputData[index]}, output={outputData[index]}"); + Assert.Fail($"input and output differ at index {index}, input={inputData[index]}, output={outputData[index]}"); } } 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 97243d4abd..e795640e23 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ConnectionExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ConnectionExceptionTest.cs @@ -144,7 +144,13 @@ public void NamedPipeInvalidConnStringTest() } [Fact] - [SkipOnTargetFramework(~TargetFrameworkMonikers.Uap)] + [SkipOnTargetFramework( + // We had defined Uap to be 0x4 in our fork/copy of XUnitExtensions. + // Now that we're using the NuGet package, I can't add enum members, + // so I kept the functionality here by casting from the old raw + // value. XUnitExtensions's TargetFrameworkMonikers currently only + // includes enum values up to 0x2, so this is safe - for now. + ~(TargetFrameworkMonikers)0x4)] public static void LocalDBNotSupportedOnUapTest() { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(@$"server=(localdb)\{DataTestUtility.LocalDbAppName}") diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs index 654d020742..e043b2253c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs @@ -30,7 +30,7 @@ public static void IntegratedAuthenticationTestWithOutConnectionPooling() TryOpenConnectionWithIntegratedAuthentication(builder.ConnectionString); } - [ActiveIssue(21707)] + [ActiveIssue("21707")] [ConditionalFact(nameof(IsIntegratedSecurityEnvironmentSet), nameof(AreConnectionStringsSetup))] public static void IntegratedAuthenticationTest_InvalidServerSPN() { 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 60e9b293f3..6059d0ec6c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs @@ -27,14 +27,20 @@ private enum InfoType private static readonly string s_localDbNamedPipeConnectionString = @$"server={GetLocalDbNamedPipe()}"; #region LocalDbTests - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework( + // We had defined Uap to be 0x4 in our fork/copy of XUnitExtensions. + // Now that we're using the NuGet package, I can't add enum members, + // so I kept the functionality here by casting from the old raw + // value. XUnitExtensions's TargetFrameworkMonikers currently only + // includes enum values up to 0x2, so this is safe - for now. + (TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void SqlLocalDbConnectionTest() { ConnectionTest(s_localDbConnectionString); } - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void LocalDBEncryptionNotSupportedTest() { @@ -43,7 +49,7 @@ public static void LocalDBEncryptionNotSupportedTest() ConnectionWithEncryptionTest(s_localDbConnectionString); } - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void LocalDBMarsTest() { @@ -51,7 +57,7 @@ public static void LocalDBMarsTest() ConnectionWithMarsTest(s_localDbConnectionString); } - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void InvalidLocalDBTest() { @@ -61,7 +67,7 @@ public static void InvalidLocalDBTest() #endregion #region SharedLocalDb tests - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDbSharedInstanceSet))] public static void SharedLocalDbEncryptionTest() { @@ -73,7 +79,7 @@ public static void SharedLocalDbEncryptionTest() } } - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDbSharedInstanceSet))] public static void SharedLocalDbMarsTest() { @@ -83,7 +89,7 @@ public static void SharedLocalDbMarsTest() } } - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDbSharedInstanceSet))] public static void SqlLocalDbSharedInstanceConnectionTest() { @@ -97,16 +103,16 @@ public static void SqlLocalDbSharedInstanceConnectionTest() #region NamedPipeTests [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP - [ActiveIssue(20245)] //pending pipeline configuration + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP + [ActiveIssue("20245")] //pending pipeline configuration public static void SqlLocalDbNamedPipeConnectionTest() { ConnectionTest(s_localDbNamedPipeConnectionString); } [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP - [ActiveIssue(20245)] //pending pipeline configuration + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP + [ActiveIssue("20245")] //pending pipeline configuration public static void LocalDBNamedPipeEncryptionNotSupportedTest() { // Encryption is not supported by SQL Local DB. @@ -115,8 +121,8 @@ public static void LocalDBNamedPipeEncryptionNotSupportedTest() } [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP - [ActiveIssue(20245)] //pending pipeline configuration + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP + [ActiveIssue("20245")] //pending pipeline configuration public static void LocalDBNamepipeMarsTest() { ConnectionWithMarsTest(s_localDbNamedPipeConnectionString); @@ -126,7 +132,7 @@ public static void LocalDBNamepipeMarsTest() #region Failures // ToDo: After adding shared memory support on managed SNI, the IsNativeSNI could be taken out - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalTheory(nameof(IsLocalDBEnvironmentSet), nameof(IsNativeSNI))] [InlineData("lpc:")] public static void SharedMemoryAndSqlLocalDbConnectionTest(string prefix) @@ -137,7 +143,7 @@ public static void SharedMemoryAndSqlLocalDbConnectionTest(string prefix) Assert.Contains("A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 41 - Cannot open a Shared Memory connection to a remote SQL server)", ex.Message); } - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [InlineData("tcp:")] [InlineData("np:")] [InlineData("undefinded:")] @@ -150,7 +156,7 @@ public static void PrefixAndSqlLocalDbConnectionTest(string prefix) Assert.Contains("A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)", ex.Message); } - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [SkipOnTargetFramework((TargetFrameworkMonikers)0x4)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void InvalidSqlLocalDbConnectionTest() { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSSessionPoolingTest/MARSSessionPoolingTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSSessionPoolingTest/MARSSessionPoolingTest.cs index b205ee0af2..4de06aeda3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSSessionPoolingTest/MARSSessionPoolingTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSSessionPoolingTest/MARSSessionPoolingTest.cs @@ -67,7 +67,7 @@ public static void MarsExecuteReader_RPC_NoGC() } // Synapse: Catalog view 'dm_exec_connections' is not supported in this version. - [ActiveIssue(11167)] + [ActiveIssue("11167")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void MarsExecuteReader_Text_WithGC() { @@ -76,7 +76,7 @@ public static void MarsExecuteReader_Text_WithGC() } // Synapse: Stored procedure sp_who does not exist or is not supported. - [ActiveIssue(8959)] + [ActiveIssue("8959")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void MarsExecuteReader_StoredProcedure_WithGC() { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs index 5d28c1b0f3..b0f18a3ab0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs @@ -688,7 +688,7 @@ FROM [{table}] AS [l] } catch (Exception e) { - Assert.False(true, "CRITIAL: Test should not fail randomly. Exception occurred: " + e.Message); + Assert.Fail("CRITIAL: Test should not fail randomly. Exception occurred: " + e.Message); } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index 6136f0bfc1..3d15953fcd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -761,7 +761,7 @@ public static void ClosedConnection_SqlParameterValueTest() } catch (Exception e) { - Assert.False(true, $"Unexpected exception occurred: {e.Message}"); + Assert.Fail($"Unexpected exception occurred: {e.Message}"); } } }); @@ -804,7 +804,7 @@ private static void RunParameterTest() return; else if ((Guid)cm.Parameters["@id2"].Value != expectedGuid) { - Assert.False(true, "CRITICAL : Unexpected data found in SqlCommand parameters, this is a MAJOR issue."); + Assert.Fail("CRITICAL : Unexpected data found in SqlCommand parameters, this is a MAJOR issue."); } } } 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 7a13fdceb5..b8882b451a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs @@ -53,7 +53,7 @@ public void TestMain() } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] - [ActiveIssue(5531)] + [ActiveIssue("5531")] public void TestPacketNumberWraparound() { // this test uses a specifically crafted sql record enumerator and data to put the TdsParserStateObject.WritePacket(byte,bool) @@ -120,7 +120,7 @@ public void TestConnectionIsSafeToReuse() cmd.CommandTimeout = 100; AddCommandParameters(cmd, parameters); new SqlDataAdapter(cmd).Fill(new("BadFunc")); - Assert.False(true, "Expected exception did not occur"); + Assert.Fail("Expected exception did not occur"); } catch (Exception e) { @@ -140,7 +140,7 @@ public void TestConnectionIsSafeToReuse() } catch (Exception e) { - Assert.False(true, $"Unexpected error occurred: {e.Message}"); + Assert.Fail($"Unexpected error occurred: {e.Message}"); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicCounterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicCounterTest.cs index b62f2f1f53..e451091315 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicCounterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicCounterTest.cs @@ -53,7 +53,7 @@ public async void ValidateRetryCount_SqlCommand_Async(string methodName, int num default: break; } - Assert.False(true, "Exception did not occur."); + Assert.Fail("Exception did not occur."); } catch { @@ -103,7 +103,7 @@ public void ValidateRetryCount_SqlCommand_Sync(string methodName, int numOfTries default: break; } - Assert.False(true, "Exception did not occur."); + Assert.Fail("Exception did not occur."); } catch { 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 dc467e3775..cd3aa3e8cc 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs @@ -107,7 +107,7 @@ public void RetryExecuteCancel(string cnnString, SqlRetryLogicBaseProvider provi } } - [ActiveIssue(14588)] + [ActiveIssue("14588")] [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public void RetryExecuteWithTransScope(string cnnString, SqlRetryLogicBaseProvider provider) @@ -233,7 +233,7 @@ public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLo } } - [ActiveIssue(14325)] + [ActiveIssue("14325")] // avoid creating a new database in Azure [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyDropDB), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] 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 d6f1e39f77..301ed95d74 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)] + [ActiveIssue("14590", TestPlatforms.Windows)] // avoid creating a new database in Azure [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyLongRunner), parameters: new object[] { 10 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] @@ -100,7 +100,7 @@ public void CreateDatabaseWhileTryingToConnect(string cnnString, SqlRetryLogicBa Assert.True(currentRetries > 0); } - [ActiveIssue(25147)] + [ActiveIssue("25147")] [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider provider) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SplitPacketTest/SplitPacketTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SplitPacketTest/SplitPacketTest.cs index 4d4313a000..1147e704ca 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SplitPacketTest/SplitPacketTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SplitPacketTest/SplitPacketTest.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { - [ActiveIssue(5538)] // Only testable on localhost + [ActiveIssue("5538")] // Only testable on localhost public class SplitPacketTest { private int Port = -1; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs index e720ff96f1..f7cb3e7490 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs @@ -297,7 +297,7 @@ public void OrderHintTransactionTest() } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ActiveIssue(12219)] + [ActiveIssue("12219")] public void OrderHintIdentityColumnTest() { OrderHintIdentityColumn.Test(_connStr, AddGuid("SqlBulkCopyTest_OrderHintIdentityColumn")); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs index 601bffa42a..0928a71569 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs @@ -157,7 +157,7 @@ public static void TimeoutCancel() TimeoutCancel(tcp_connStr); } - [ActiveIssue(12167)] + [ActiveIssue("12167")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [PlatformSpecific(TestPlatforms.Windows)] public static void TimeoutCancelNP() @@ -178,14 +178,14 @@ public static void CancelAndDisposePreparedCommandNP() CancelAndDisposePreparedCommand(np_connStr); } - [ActiveIssue(5541)] + [ActiveIssue("5541")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void TimeOutDuringRead() { TimeOutDuringRead(tcp_connStr); } - [ActiveIssue(5541)] + [ActiveIssue("5541")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [PlatformSpecific(TestPlatforms.Windows)] public static void TimeOutDuringReadNP() @@ -306,7 +306,7 @@ IF EXISTS (SELECT name FROM msdb.dbo.sysalerts WHERE name = N'{alertName}') { if (retryAttempt >= 3 || e.Message.Contains("The transaction operation cannot be performed")) { - Assert.False(true, $"Retry Attempt: {retryAttempt} | Unexpected Exception occurred: {e.Message}"); + Assert.Fail($"Retry Attempt: {retryAttempt} | Unexpected Exception occurred: {e.Message}"); } else { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs index 38cd77a89e..c2a4ab79e8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs @@ -122,7 +122,7 @@ public void SqlDependencyStartStopTest() } catch (Exception e) { - Assert.True(false, e.Message); + Assert.Fail(e.Message); } } @@ -152,7 +152,7 @@ public void SqlDependencyStartStopDefaultTest() } catch (Exception e) { - Assert.True(false, e.Message); + Assert.Fail(e.Message); } } 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 11827aa15f..ed7c5b5b8a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs @@ -226,7 +226,7 @@ public void Test_SingleDependency_CustomQueue_SqlAuth() /// /// SqlDependecy premature timeout /// - [ActiveIssue(5539)] + [ActiveIssue("5539")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void Test_SingleDependency_Timeout() { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs index ade9f41844..30244df808 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionEnlistmentTest.cs @@ -62,7 +62,7 @@ public static void TestEnlistmentPrepare_TxScopeComplete() connection.Open(); System.Transactions.Transaction.Current.EnlistDurable(EnlistmentForPrepare.s_id, new EnlistmentForPrepare(), EnlistmentOptions.None); txScope.Complete(); - Assert.False(true, "Expected exception not thrown."); + Assert.Fail("Expected exception not thrown."); }); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs index 5f17036b4f..469c895a61 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs @@ -11,7 +11,7 @@ public class UdtBulkCopyTest { private string _connStr; - [ActiveIssue(5535)] + [ActiveIssue("5535")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void RunCopyTest() { 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 ef38e43dda..6a20db8c5f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { - [ActiveIssue(5536)] + [ActiveIssue("5536")] // TODO Synapse: Remove dependency from UDTTest Database public class UdtTest2 { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/WeakRefTestYukonSpecific/WeakRefTestYukonSpecific.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/WeakRefTestYukonSpecific/WeakRefTestYukonSpecific.cs index 5835bb9e69..bf0567df82 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/WeakRefTestYukonSpecific/WeakRefTestYukonSpecific.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/WeakRefTestYukonSpecific/WeakRefTestYukonSpecific.cs @@ -17,7 +17,7 @@ public static class WeakRefTestYukonSpecific private const int CONCURRENT_COMMANDS = 5; // TODO Synapse: Remove dependency on Northwind database - [ActiveIssue(6643, TestPlatforms.AnyUnix)] + [ActiveIssue("6643", TestPlatforms.AnyUnix)] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void TestReaderMars() { diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/Microsoft.Data.SqlClient.ExtUtilities.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/Microsoft.Data.SqlClient.ExtUtilities.csproj index ab120d549f..7219ca2c8b 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/Microsoft.Data.SqlClient.ExtUtilities.csproj +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/Microsoft.Data.SqlClient.ExtUtilities.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj index 379dd4cb96..1ca32f6754 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj @@ -14,8 +14,9 @@ PreserveNewest + - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json new file mode 100644 index 0000000000..42755c93ec --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "diagnosticMessages": true, + "parallelizeAssembly": true, + "shadowCopy": false, + "printMaxEnumerableLength": 0, + "printMaxStringLength": 0, + "showLiveOutput": false +} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ActiveIssueAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ActiveIssueAttribute.cs deleted file mode 100644 index 97c9e4b521..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ActiveIssueAttribute.cs +++ /dev/null @@ -1,24 +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 Xunit.Sdk; - -namespace Xunit -{ - /// - /// Apply this attribute to your test method to specify an active issue. - /// - [TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.ActiveIssueDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] - public class ActiveIssueAttribute : Attribute, ITraitAttribute - { - public ActiveIssueAttribute(int issueNumber, TestPlatforms platforms) { } - public ActiveIssueAttribute(string issue, TestPlatforms platforms) { } - public ActiveIssueAttribute(int issueNumber, TargetFrameworkMonikers framework) { } - public ActiveIssueAttribute(string issue, TargetFrameworkMonikers framework) { } - public ActiveIssueAttribute(int issueNumber, TestPlatforms platforms = TestPlatforms.Any, TargetFrameworkMonikers framework = (TargetFrameworkMonikers)0) { } - public ActiveIssueAttribute(string issue, TestPlatforms platforms = TestPlatforms.Any, TargetFrameworkMonikers framework = (TargetFrameworkMonikers)0) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalClassAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalClassAttribute.cs deleted file mode 100644 index 6ca0fc3bf0..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalClassAttribute.cs +++ /dev/null @@ -1,23 +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 Xunit.Sdk; - -namespace Xunit -{ - [TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.ConditionalClassDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public sealed class ConditionalClassAttribute : Attribute, ITraitAttribute - { - public Type CalleeType { get; private set; } - public string[] ConditionMemberNames { get; private set; } - - public ConditionalClassAttribute(Type calleeType, params string[] conditionMemberNames) - { - CalleeType = calleeType; - ConditionMemberNames = conditionMemberNames; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalFactAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalFactAttribute.cs deleted file mode 100644 index 54996c0449..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalFactAttribute.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; -using Xunit.Sdk; - -namespace Xunit -{ - [XunitTestCaseDiscoverer("Microsoft.DotNet.XUnitExtensions.ConditionalFactDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public sealed class ConditionalFactAttribute : FactAttribute - { - public Type CalleeType { get; private set; } - public string[] ConditionMemberNames { get; private set; } - - public ConditionalFactAttribute(Type calleeType, params string[] conditionMemberNames) - { - CalleeType = calleeType; - ConditionMemberNames = conditionMemberNames; - } - - public ConditionalFactAttribute(params string[] conditionMemberNames) - { - ConditionMemberNames = conditionMemberNames; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalTheoryAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalTheoryAttribute.cs deleted file mode 100644 index f924102cab..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/ConditionalTheoryAttribute.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; -using Xunit.Sdk; - -namespace Xunit -{ - [XunitTestCaseDiscoverer("Microsoft.DotNet.XUnitExtensions.ConditionalTheoryDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public sealed class ConditionalTheoryAttribute : TheoryAttribute - { - public Type CalleeType { get; private set; } - public string[] ConditionMemberNames { get; private set; } - - public ConditionalTheoryAttribute(Type calleeType, params string[] conditionMemberNames) - { - CalleeType = calleeType; - ConditionMemberNames = conditionMemberNames; - } - - public ConditionalTheoryAttribute(params string[] conditionMemberNames) - { - ConditionMemberNames = conditionMemberNames; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/OuterLoopAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/OuterLoopAttribute.cs deleted file mode 100644 index 6194eadbd6..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/OuterLoopAttribute.cs +++ /dev/null @@ -1,20 +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 Xunit.Sdk; - -namespace Xunit -{ - /// - /// Apply this attribute to your test method to specify a outer-loop category. - /// - [TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.OuterLoopTestsDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] - public class OuterLoopAttribute : Attribute, ITraitAttribute - { - public OuterLoopAttribute() { } - public OuterLoopAttribute(string reason) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/PlatformSpecificAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/PlatformSpecificAttribute.cs deleted file mode 100644 index 0f4d6bd025..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/PlatformSpecificAttribute.cs +++ /dev/null @@ -1,19 +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 Xunit.Sdk; - -namespace Xunit -{ - /// - /// Apply this attribute to your test method to specify this is a platform specific test. - /// - [TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.PlatformSpecificDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] - public class PlatformSpecificAttribute : Attribute, ITraitAttribute - { - public PlatformSpecificAttribute(TestPlatforms platforms) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnCoreClrAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnCoreClrAttribute.cs deleted file mode 100644 index abf1fda557..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnCoreClrAttribute.cs +++ /dev/null @@ -1,21 +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 Xunit.Sdk; - -namespace Xunit -{ - [TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.SkipOnCoreClrDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public class SkipOnCoreClrAttribute : Attribute, ITraitAttribute - { - internal SkipOnCoreClrAttribute() { } - - public SkipOnCoreClrAttribute(string reason, TestPlatforms testPlatforms) { } - public SkipOnCoreClrAttribute(string reason, RuntimeStressTestModes testMode) { } - public SkipOnCoreClrAttribute(string reason, TestPlatforms testPlatforms, RuntimeStressTestModes testMode) { } - public SkipOnCoreClrAttribute(string reason) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnMonoAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnMonoAttribute.cs deleted file mode 100644 index b535ebf1c3..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnMonoAttribute.cs +++ /dev/null @@ -1,18 +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 Xunit; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions.Attributes -{ - [TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.SkipOnMonoDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public class SkipOnMonoAttribute : Attribute, ITraitAttribute - { - internal SkipOnMonoAttribute() { } - public SkipOnMonoAttribute(string reason, TestPlatforms testPlatforms = TestPlatforms.Any) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnTargetFrameworkAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnTargetFrameworkAttribute.cs deleted file mode 100644 index 6ed78b5e0e..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/SkipOnTargetFrameworkAttribute.cs +++ /dev/null @@ -1,19 +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 Xunit.Sdk; - -namespace Xunit -{ - /// - /// Apply this attribute to your test method to specify this is a platform specific test. - /// - [TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.SkipOnTargetFrameworkDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] - public class SkipOnTargetFrameworkAttribute : Attribute, ITraitAttribute - { - public SkipOnTargetFrameworkAttribute(TargetFrameworkMonikers platform, string reason = null) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/TestCategoryAttribute.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/TestCategoryAttribute.cs deleted file mode 100644 index 013931dc00..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Attributes/TestCategoryAttribute.cs +++ /dev/null @@ -1,19 +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 Xunit.Sdk; - -namespace Xunit -{ - /// - /// Apply this attribute to your test method to specify the test category. - /// - [TraitDiscoverer("Microsoft.DotNet.XUnitExtensions.TestCategoryDiscoverer", "Microsoft.DotNet.XUnitExtensions")] - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] - public class TestCategoryAttribute : Attribute, ITraitAttribute - { - public TestCategoryAttribute(string category) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/ConditionalDiscovererException.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/ConditionalDiscovererException.cs deleted file mode 100644 index 4a7e80b6f7..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/ConditionalDiscovererException.cs +++ /dev/null @@ -1,13 +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.DotNet.XUnitExtensions -{ - internal class ConditionalDiscovererException : Exception - { - public ConditionalDiscovererException(string message) : base(message) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/DesktopTraceListener.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/DesktopTraceListener.cs deleted file mode 100644 index 7adc2fbb53..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/DesktopTraceListener.cs +++ /dev/null @@ -1,32 +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; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// - /// Trace Listener for corefx Desktop test execution to avoid showing assert pop-ups and making the test fail when an Assert fails. - /// - public class DesktopTestTraceListener : DefaultTraceListener - { - /// - /// Override of to handle Assert failures with custom behavior. - /// When an Assert failure happens during test execution we will rather throw a DebugAssertException so that the test fails and we have a full StackTrace. - /// - public override void Fail(string message, string detailMessage) - { - throw new DebugAssertException(message, detailMessage); - } - - private sealed class DebugAssertException : Exception - { - internal DebugAssertException(string message, string detailMessage) : - base(message + Environment.NewLine + detailMessage) - { - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/DiscovererHelpers.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/DiscovererHelpers.cs deleted file mode 100644 index 5dab843fab..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/DiscovererHelpers.cs +++ /dev/null @@ -1,26 +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.Runtime.InteropServices; -using Xunit; - -namespace Microsoft.DotNet.XUnitExtensions -{ - internal static class DiscovererHelpers - { - internal static bool TestPlatformApplies(TestPlatforms platforms) => -#if NETCOREAPP - (platforms.HasFlag(TestPlatforms.FreeBSD) && RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD"))) || - (platforms.HasFlag(TestPlatforms.Linux) && RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) || - (platforms.HasFlag(TestPlatforms.NetBSD) && RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD"))) || - (platforms.HasFlag(TestPlatforms.OSX) && RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) || - (platforms.HasFlag(TestPlatforms.Windows) && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); -# else - (platforms.HasFlag(TestPlatforms.Windows) && (int)Environment.OSVersion.Platform == 2) || - (platforms.HasFlag(TestPlatforms.Linux) && (int)Environment.OSVersion.Platform == 4) || - (platforms.HasFlag(TestPlatforms.OSX) && (int)Environment.OSVersion.Platform == 6); -#endif - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ActiveIssueDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ActiveIssueDiscoverer.cs deleted file mode 100644 index 0e45c581bd..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ActiveIssueDiscoverer.cs +++ /dev/null @@ -1,63 +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.Generic; -using System.Diagnostics; -using System.Linq; -using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// - /// This class discovers all of the tests and test classes that have - /// applied the ActiveIssue attribute - /// - public class ActiveIssueDiscoverer : ITraitDiscoverer - { - /// - /// Gets the trait values from the Category attribute. - /// - /// The trait attribute containing the trait values. - /// The trait values. - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - IEnumerable ctorArgs = traitAttribute.GetConstructorArguments(); - Debug.Assert(ctorArgs.Count() >= 2); - - string issue = ctorArgs.First().ToString(); - TestPlatforms platforms = TestPlatforms.Any; - TargetFrameworkMonikers frameworks = (TargetFrameworkMonikers)0; - - foreach (object arg in ctorArgs.Skip(1)) // First argument is the issue number. - { - if (arg is TestPlatforms) - { - platforms = (TestPlatforms)arg; - } - else if (arg is TargetFrameworkMonikers) - { - frameworks = (TargetFrameworkMonikers)arg; - } - } - - if (DiscovererHelpers.TestPlatformApplies(platforms)) - { - if (frameworks.HasFlag(TargetFrameworkMonikers.Netcoreapp)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonNetcoreappTest); - if (frameworks.HasFlag(TargetFrameworkMonikers.NetFramework)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonNetfxTest); - if (frameworks.HasFlag(TargetFrameworkMonikers.Uap)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonUapTest); - if (frameworks.HasFlag(TargetFrameworkMonikers.Mono)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonMonoTest); - if (frameworks == (TargetFrameworkMonikers)0) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.Failing); - - yield return new KeyValuePair(XunitConstants.ActiveIssue, issue); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalClassDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalClassDiscoverer.cs deleted file mode 100644 index 7703101313..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalClassDiscoverer.cs +++ /dev/null @@ -1,70 +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.Diagnostics; -using System.Linq; -using System.Reflection; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// - /// This class discovers all of the tests and test classes that have - /// applied the ConditionalClass attribute - /// - public class ConditionalClassDiscoverer : ITraitDiscoverer - { - /// - /// Gets the trait values from the Category attribute. - /// - /// The trait attribute containing the trait values. - /// The trait values. - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - // If evaluated to false, skip the test class entirely. - if (!EvaluateParameterHelper(traitAttribute)) - { - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.Failing); - } - } - - internal static bool EvaluateParameterHelper(IAttributeInfo traitAttribute) - { - // Parse the traitAttribute. We make sure it contains two parts: - // 1. Type 2. nameof(conditionMemberName) - object[] conditionArguments = traitAttribute.GetConstructorArguments().ToArray(); - Debug.Assert(conditionArguments.Count() == 2); - - Type calleeType = null; - string[] conditionMemberNames = null; - - if (ConditionalTestDiscoverer.CheckInputToSkipExecution(conditionArguments, ref calleeType, ref conditionMemberNames)) - { - return true; - } - - foreach (string entry in conditionMemberNames) - { - // Null condition member names are silently tolerated. - if (string.IsNullOrWhiteSpace(entry)) - continue; - - MethodInfo conditionMethodInfo = ConditionalTestDiscoverer.LookupConditionalMethod(calleeType, entry); - if (conditionMethodInfo == null) - { - throw new InvalidOperationException($"Unable to get MethodInfo, please check input for {entry}."); - } - - // If one of the conditions is false, then return the category failing trait. - if (!(bool)conditionMethodInfo.Invoke(null, null)) - return false; - } - - return true; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalFactDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalFactDiscoverer.cs deleted file mode 100644 index 3b9e368ae9..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalFactDiscoverer.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.Linq; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - public class ConditionalFactDiscoverer : FactDiscoverer - { - public ConditionalFactDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { } - - protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute) - { - if (ConditionalTestDiscoverer.TryEvaluateSkipConditions(discoveryOptions, DiagnosticMessageSink, testMethod, factAttribute.GetConstructorArguments().ToArray(), out string skipReason, out ExecutionErrorTestCase errorTestCase)) - { - return skipReason != null - ? (IXunitTestCase)new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) - : new SkippedFactTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod); // Test case skippable at runtime. - } - - return errorTestCase; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalTestDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalTestDiscoverer.cs deleted file mode 100644 index eac5543402..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalTestDiscoverer.cs +++ /dev/null @@ -1,180 +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.Linq; -using System.Reflection; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - // Internal helper class for code common to conditional test discovery through - // [ConditionalFact] and [ConditionalTheory] - internal static class ConditionalTestDiscoverer - { - // This helper method evaluates the given condition member names for a given set of test cases. - // If any condition member evaluates to 'false', the test cases are marked to be skipped. - // The skip reason is the collection of all the condition members that evaluated to 'false'. - internal static string EvaluateSkipConditions(ITestMethod testMethod, object[] conditionArguments) - { - Type calleeType = null; - string[] conditionMemberNames = null; - - if (CheckInputToSkipExecution(conditionArguments, ref calleeType, ref conditionMemberNames, testMethod)) - return null; - - MethodInfo testMethodInfo = testMethod.Method.ToRuntimeMethod(); - Type testMethodDeclaringType = testMethodInfo.DeclaringType; - List falseConditions = new List(conditionMemberNames.Count()); - - foreach (string entry in conditionMemberNames) - { - string conditionMemberName = entry; - - // Null condition member names are silently tolerated - if (string.IsNullOrWhiteSpace(conditionMemberName)) - { - continue; - } - - Type declaringType; - - if (calleeType != null) - { - declaringType = calleeType; - } - else - { - declaringType = testMethodDeclaringType; - - string[] symbols = conditionMemberName.Split('.'); - if (symbols.Length == 2) - { - conditionMemberName = symbols[1]; - ITypeInfo type = testMethod.TestClass.Class.Assembly.GetTypes(false).Where(t => t.Name.Contains(symbols[0])).FirstOrDefault(); - if (type != null) - { - declaringType = type.ToRuntimeType(); - } - } - } - - MethodInfo conditionMethodInfo; - if ((conditionMethodInfo = LookupConditionalMethod(declaringType, conditionMemberName)) == null) - { - throw new ConditionalDiscovererException(GetFailedLookupString(conditionMemberName, declaringType)); - } - - // In the case of multiple conditions, collect the results of all - // of them to produce a summary skip reason. - try - { - if (!(bool)conditionMethodInfo.Invoke(null, null)) - { - falseConditions.Add(conditionMemberName); - } - } - catch (Exception exc) - { - falseConditions.Add($"{conditionMemberName} ({exc.GetType().Name})"); - } - } - - // Compose a summary of all conditions that returned false. - if (falseConditions.Count > 0) - { - return string.Format("Condition(s) not met: \"{0}\"", string.Join("\", \"", falseConditions)); - } - - // No conditions returned false (including the absence of any conditions). - return null; - } - - internal static bool TryEvaluateSkipConditions(ITestFrameworkDiscoveryOptions discoveryOptions, IMessageSink diagnosticMessageSink, ITestMethod testMethod, object[] conditionArguments, out string skipReason, out ExecutionErrorTestCase errorTestCase) - { - skipReason = null; - errorTestCase = null; - try - { - skipReason = EvaluateSkipConditions(testMethod, conditionArguments); - return true; - } - catch (ConditionalDiscovererException e) - { - errorTestCase = new ExecutionErrorTestCase( - diagnosticMessageSink, - discoveryOptions.MethodDisplayOrDefault(), - discoveryOptions.MethodDisplayOptionsOrDefault(), - testMethod, - e.Message); - return false; - } - } - - internal static string GetFailedLookupString(string name, Type type) - { - return - $"An appropriate member '{name}' could not be found. " + - $"The conditional method needs to be a static method or property on the type {type} or any ancestor, " + - "of any visibility, accepting zero arguments, and having a return type of Boolean."; - } - - internal static MethodInfo LookupConditionalMethod(Type t, string name) - { - if (t == null || name == null) - return null; - - TypeInfo ti = t.GetTypeInfo(); - - MethodInfo mi = ti.GetDeclaredMethod(name); - if (mi != null && mi.IsStatic && mi.GetParameters().Length == 0 && mi.ReturnType == typeof(bool)) - return mi; - - PropertyInfo pi = ti.GetDeclaredProperty(name); - if (pi != null && pi.PropertyType == typeof(bool) && pi.GetMethod != null && pi.GetMethod.IsStatic && pi.GetMethod.GetParameters().Length == 0) - return pi.GetMethod; - - return LookupConditionalMethod(ti.BaseType, name); - } - - internal static bool CheckInputToSkipExecution(object[] conditionArguments, ref Type calleeType, ref string[] conditionMemberNames, ITestMethod testMethod = null) - { - // A null or empty list of conditionArguments is treated as "no conditions". - // and the test cases will be executed. - // Example: [ConditionalClass()] - if (conditionArguments == null || conditionArguments.Length == 0) - return true; - - calleeType = conditionArguments[0] as Type; - if (calleeType != null) - { - if (conditionArguments.Length < 2) - { - // [ConditionalFact(typeof(x))] no provided methods. - return true; - } - - // [ConditionalFact(typeof(x), "MethodName")] - conditionMemberNames = conditionArguments[1] as string[]; - } - else - { - // For [ConditionalClass], unable to get the Type info. All test cases will be executed. - if (testMethod == null) - return true; - - // [ConditionalFact("MethodName")] - conditionMemberNames = conditionArguments[0] as string[]; - } - - // [ConditionalFact((string[]) null)] - if (conditionMemberNames == null || conditionMemberNames.Count() == 0) - return true; - - return false; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalTheoryDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalTheoryDiscoverer.cs deleted file mode 100644 index 4bafc37a56..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/ConditionalTheoryDiscoverer.cs +++ /dev/null @@ -1,59 +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.Generic; -using System.Linq; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - public class ConditionalTheoryDiscoverer : TheoryDiscoverer - { - private readonly Dictionary _conditionCache; - - public ConditionalTheoryDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) - { - _conditionCache = new Dictionary(); - } - - protected override IEnumerable CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) - { - if (ConditionalTestDiscoverer.TryEvaluateSkipConditions(discoveryOptions, DiagnosticMessageSink, testMethod, theoryAttribute.GetConstructorArguments().ToArray(), out string skipReason, out ExecutionErrorTestCase errorTestCase)) - { - return skipReason != null - ? new[] { new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) } - : new IXunitTestCase[] { new SkippedTheoryTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) }; // Theory skippable at runtime. - } - - return new IXunitTestCase[] { errorTestCase }; - } - - protected override IEnumerable CreateTestCasesForDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow) - { - IMethodInfo methodInfo = testMethod.Method; - List skippedTestCase = new List(); - - if (!_conditionCache.TryGetValue(methodInfo, out string skipReason)) - { - if (!ConditionalTestDiscoverer.TryEvaluateSkipConditions(discoveryOptions, DiagnosticMessageSink, testMethod, theoryAttribute.GetConstructorArguments().ToArray(), out skipReason, out ExecutionErrorTestCase errorTestCase)) - { - return new IXunitTestCase[] { errorTestCase }; - } - - _conditionCache.Add(methodInfo, skipReason); - - if (skipReason != null) - { - // If this is the first time we evalute the condition we return a SkippedTestCase to avoid printing a skip for every inline-data. - skippedTestCase.Add(new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod)); - } - } - - return skipReason != null ? - (IEnumerable)skippedTestCase - : new[] { new SkippedFactTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, dataRow) }; // Test case skippable at runtime. - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/OuterLoopTestsDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/OuterLoopTestsDiscoverer.cs deleted file mode 100644 index df6e168fd5..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/OuterLoopTestsDiscoverer.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.Collections.Generic; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// - /// This class discovers all of the tests and test classes that have - /// applied the OuterLoop attribute - /// - public class OuterLoopTestsDiscoverer : ITraitDiscoverer - { - /// - /// Gets the trait values from the Category attribute. - /// - /// The trait attribute containing the trait values. - /// The trait values. - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.OuterLoop); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/PlatformSpecificDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/PlatformSpecificDiscoverer.cs deleted file mode 100644 index 1e7b5fe210..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/PlatformSpecificDiscoverer.cs +++ /dev/null @@ -1,39 +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.Generic; -using System.Linq; -using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// - /// This class discovers all of the tests and test classes that have - /// applied the PlatformSpecific attribute - /// - public class PlatformSpecificDiscoverer : ITraitDiscoverer - { - /// - /// Gets the trait values from the Category attribute. - /// - /// The trait attribute containing the trait values. - /// The trait values. - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - TestPlatforms platforms = (TestPlatforms)traitAttribute.GetConstructorArguments().First(); - if (!platforms.HasFlag(TestPlatforms.Windows)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonWindowsTest); - if (!platforms.HasFlag(TestPlatforms.Linux)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonLinuxTest); - if (!platforms.HasFlag(TestPlatforms.OSX)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonOSXTest); - if (!platforms.HasFlag(TestPlatforms.FreeBSD)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonFreeBSDTest); - if (!platforms.HasFlag(TestPlatforms.NetBSD)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonNetBSDTest); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnCoreClrDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnCoreClrDiscoverer.cs deleted file mode 100644 index d3bc7529bb..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnCoreClrDiscoverer.cs +++ /dev/null @@ -1,103 +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.Linq; -using System.Reflection; -using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - public class SkipOnCoreClrDiscoverer : ITraitDiscoverer - { - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - if (!SkipOnMonoDiscoverer.IsMonoRuntime) - { - TestPlatforms testPlatforms = TestPlatforms.Any; - RuntimeStressTestModes stressMode = RuntimeStressTestModes.Any; - foreach (object arg in traitAttribute.GetConstructorArguments().Skip(1)) // We skip the first one as it is the reason - { - if (arg is TestPlatforms tp) - { - testPlatforms = tp; - } - else if (arg is RuntimeStressTestModes rstm) - { - stressMode = rstm; - } - } - - if (DiscovererHelpers.TestPlatformApplies(testPlatforms) && StressModeApplies(stressMode)) - { - if (IsCheckedRuntime() || (IsRuntimeStressTesting && !stressMode.HasFlag(RuntimeStressTestModes.CheckedRuntime))) - { - return new[] { new KeyValuePair(XunitConstants.Category, XunitConstants.Failing) }; - } - } - } - - return Array.Empty>(); - } - - // Order here matters as some env variables may appear in multiple modes - private static bool StressModeApplies(RuntimeStressTestModes stressMode) => - stressMode == RuntimeStressTestModes.Any || - (stressMode.HasFlag(RuntimeStressTestModes.GCStress3) && IsGCStress3) || - (stressMode.HasFlag(RuntimeStressTestModes.GCStressC) && IsGCStressC) || - (stressMode.HasFlag(RuntimeStressTestModes.ZapDisable) && IsZapDisable) || - (stressMode.HasFlag(RuntimeStressTestModes.TailcallStress) && IsTailCallStress) || - (stressMode.HasFlag(RuntimeStressTestModes.JitStressRegs) && IsJitStressRegs) || - (stressMode.HasFlag(RuntimeStressTestModes.JitStress) && IsJitStress) || - (stressMode.HasFlag(RuntimeStressTestModes.JitMinOpts) && IsJitMinOpts) || - stressMode == RuntimeStressTestModes.CheckedRuntime; // if checked runtime is the only flag, all stress modes apply. - - // Order here matters as some env variables may appear in multiple modes - private static bool IsRuntimeStressTesting => - IsGCStress3 || - IsGCStressC || - IsZapDisable || - IsTailCallStress || - IsJitStressRegs || - IsJitStress || - IsJitMinOpts; - - private static string GetEnvironmentVariableValue(string name) => Environment.GetEnvironmentVariable(name) ?? "0"; - - private static bool IsJitStress => !string.Equals(GetEnvironmentVariableValue("COMPlus_JitStress"), "0", StringComparison.InvariantCulture); - - private static bool IsJitStressRegs => !string.Equals(GetEnvironmentVariableValue("COMPlus_JitStressRegs"), "0", StringComparison.InvariantCulture); - - private static bool IsJitMinOpts => string.Equals(GetEnvironmentVariableValue("COMPlus_JITMinOpts"), "1", StringComparison.InvariantCulture); - - private static bool IsTailCallStress => string.Equals(GetEnvironmentVariableValue("COMPlus_TailcallStress"), "1", StringComparison.InvariantCulture); - - private static bool IsZapDisable => string.Equals(GetEnvironmentVariableValue("COMPlus_ZapDisable"), "1", StringComparison.InvariantCulture); - - private static bool IsGCStress3 => CompareGCStressModeAsLower(GetEnvironmentVariableValue("COMPlus_GCStress"), "0x3", "3"); - - private static bool IsGCStressC => CompareGCStressModeAsLower(GetEnvironmentVariableValue("COMPlus_GCStress"), "0xC", "C"); - - private static bool IsCheckedRuntime() - { - Assembly assembly = typeof(string).Assembly; - AssemblyConfigurationAttribute assemblyConfigurationAttribute = assembly.GetCustomAttribute(); - - return assemblyConfigurationAttribute != null && - string.Equals(assemblyConfigurationAttribute.Configuration, "Checked", StringComparison.InvariantCulture); - } - - private static bool CompareGCStressModeAsLower(string value, string first, string second) - { - value = value.ToLowerInvariant(); - return string.Equals(value, first.ToLowerInvariant(), StringComparison.InvariantCulture) || - string.Equals(value, second.ToLowerInvariant(), StringComparison.InvariantCulture) || - string.Equals(value, "0xf", StringComparison.InvariantCulture) || - string.Equals(value, "f", StringComparison.InvariantCulture); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnMonoDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnMonoDiscoverer.cs deleted file mode 100644 index 45e99abce5..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnMonoDiscoverer.cs +++ /dev/null @@ -1,39 +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.Linq; -using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - public class SkipOnMonoDiscoverer : ITraitDiscoverer - { - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - if (IsMonoRuntime) - { - TestPlatforms testPlatforms = TestPlatforms.Any; - - // Last argument is either the TestPlatform or the test platform to skip the test on. - if (traitAttribute.GetConstructorArguments().LastOrDefault() is TestPlatforms tp) - { - testPlatforms = tp; - } - - if (DiscovererHelpers.TestPlatformApplies(testPlatforms)) - { - return new[] { new KeyValuePair(XunitConstants.Category, XunitConstants.Failing) }; - } - } - - return Array.Empty>(); - } - - public static bool IsMonoRuntime => Type.GetType("Mono.RuntimeStructs") != null; - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnTargetFrameworkDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnTargetFrameworkDiscoverer.cs deleted file mode 100644 index 7e031f2650..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/SkipOnTargetFrameworkDiscoverer.cs +++ /dev/null @@ -1,37 +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.Generic; -using System.Linq; -using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// - /// This class discovers all of the tests and test classes that have - /// applied the TestOnTargetFrameworkDiscoverer attribute - /// - public class SkipOnTargetFrameworkDiscoverer : ITraitDiscoverer - { - /// - /// Gets the trait values from the Category attribute. - /// - /// The trait attribute containing the trait values. - /// The trait values. - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - TargetFrameworkMonikers frameworks = (TargetFrameworkMonikers)traitAttribute.GetConstructorArguments().First(); - if (frameworks.HasFlag(TargetFrameworkMonikers.Netcoreapp)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonNetcoreappTest); - if (frameworks.HasFlag(TargetFrameworkMonikers.NetFramework)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonNetfxTest); - if (frameworks.HasFlag(TargetFrameworkMonikers.Uap)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonUapTest); - if (frameworks.HasFlag(TargetFrameworkMonikers.Mono)) - yield return new KeyValuePair(XunitConstants.Category, XunitConstants.NonMonoTest); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/TestCategoryDiscoverer.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/TestCategoryDiscoverer.cs deleted file mode 100644 index 4f30e9c42d..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Discoverers/TestCategoryDiscoverer.cs +++ /dev/null @@ -1,31 +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.Generic; -using System.Linq; -using Xunit.Abstractions; -using Xunit.Sdk; - -/// -/// This is a replacement for the MSTest [TestCategoryAttribute] on xunit -/// xunit does not have the concept of Category for tests and instead, the have [TraitAttribute(string key, string value)] -/// If we replace the MSTest [TestCategoryAttribute] for the [TestCategory("BVT")], we will surely fall at some time in cases -/// where people will typo on the "Category" key part of the Trait. -/// On order to achieve the same behaviour as on MSTest, a custom [TestCategory] was created -/// to mimic the MSTest one and avoid replace it on every existent test. -/// The tests can be filtered by xunit runners by usage of "-trait" on the command line with the expression like -/// -trait "Category=BVT" for example that will only run the tests with [TestCategory("BVT")] on it. -/// - -namespace Microsoft.DotNet.XUnitExtensions -{ - public class TestCategoryDiscoverer : ITraitDiscoverer - { - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - var ctorArgs = traitAttribute.GetConstructorArguments().ToList(); - yield return new KeyValuePair("Category", ctorArgs[0].ToString()); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/TheoryExtensions.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/TheoryExtensions.cs deleted file mode 100644 index 110af4dedb..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Extensions/TheoryExtensions.cs +++ /dev/null @@ -1,38 +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.Collections.Generic; -using System.Linq; -using Xunit; - -namespace Microsoft.DotNet.XUnitExtensions -{ - public static class TheoryExtensions - { - /// - /// Converts an IEnumerable into an Xunit theory compatible enumerable. - /// - public static TheoryData ToTheoryData(this IEnumerable data) - { - // Returning TheoryData rather than IEnumerable directly should - // encourage discover and usage of TheoryData classes for more - // complicated theories. Slightly easier to type as well. - return new TheoryDataAdapter(data.Select(d => new object[] { d })); - } - - private class TheoryDataAdapter : TheoryData, IEnumerable - { - private IEnumerable _data; - - public TheoryDataAdapter(IEnumerable data) - { - _data = data; - } - - public new IEnumerator GetEnumerator() => _data.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Microsoft.DotNet.XUnitExtensions.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Microsoft.DotNet.XUnitExtensions.csproj deleted file mode 100644 index a61443eafe..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Microsoft.DotNet.XUnitExtensions.csproj +++ /dev/null @@ -1,56 +0,0 @@ - - - Microsoft.DotNet.XUnitExtensions - netcoreapp - netfx - false - $(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName) - $(BinFolder)$(Configuration).$(Platform)\$(AssemblyName) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/RuntimeStressTestModes.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/RuntimeStressTestModes.cs deleted file mode 100644 index 356c7a2910..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/RuntimeStressTestModes.cs +++ /dev/null @@ -1,39 +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 Xunit -{ - [Flags] - public enum RuntimeStressTestModes - { - // Disable on any stress test mode or on checked runtime. - // Can't be ~0 as that would include CheckedRuntime flag, which would break the case - // where you want to disable in all (including release stress test). - Any = 0, - - // JitStress, JitStressRegs, JitMinOpts and TailcallStress enable - // various modes in the JIT that cause us to exercise more code paths, - // and generate different kinds of code - JitStress = 1, // COMPlus_JitStress is set. - JitStressRegs = 1 << 1, // COMPlus_JitStressRegs is set. - JitMinOpts = 1 << 2, // COMPlus_JITMinOpts is set. - TailcallStress = 1 << 3, // COMPlus_TailcallStress is set. - - // ZapDisable says to not use NGEN or ReadyToRun images. - // This means we JIT everything. - ZapDisable = 1 << 4, // COMPlus_ZapDisable is set. - - // GCStress3 forces a GC at various locations, typically transitions - // to/from the VM from managed code. - GCStress3 = 1 << 5, // COMPlus_GCStress includes mode 0x3. - - // GCStressC forces a GC at every JIT-generated code instruction, - // including in NGEN/ReadyToRun code. - GCStressC = 1 << 6, // COMPlus_GCStress includes mode 0xC. - CheckedRuntime = 1 << 7, // Disable only when running on checked runtime. - AnyGCStress = GCStress3 | GCStressC // Disable when any GCStress is exercised. - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedFactTestCase.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedFactTestCase.cs deleted file mode 100644 index 9c05dd4915..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedFactTestCase.cs +++ /dev/null @@ -1,39 +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.Threading; -using System.Threading.Tasks; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// Wraps RunAsync for ConditionalFact. - public class SkippedFactTestCase : XunitTestCase - { - [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes", error: true)] - public SkippedFactTestCase() { } - - public SkippedFactTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, object[] testMethodArguments = null) - : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments) { } - - public override async Task RunAsync(IMessageSink diagnosticMessageSink, - IMessageBus messageBus, - object[] constructorArguments, - ExceptionAggregator aggregator, - CancellationTokenSource cancellationTokenSource) - { - SkippedTestMessageBus skipMessageBus = new SkippedTestMessageBus(messageBus); - var result = await base.RunAsync(diagnosticMessageSink, skipMessageBus, constructorArguments, aggregator, cancellationTokenSource); - if (skipMessageBus.SkippedTestCount > 0) - { - result.Failed -= skipMessageBus.SkippedTestCount; - result.Skipped += skipMessageBus.SkippedTestCount; - } - - return result; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTestCase.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTestCase.cs deleted file mode 100644 index 5f1a9079dc..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTestCase.cs +++ /dev/null @@ -1,48 +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 Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// Wraps another test case that should be skipped. - internal sealed class SkippedTestCase : XunitTestCase - { - private string _skipReason; - - [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public SkippedTestCase() : base() - { - } - - public SkippedTestCase( - string skipReason, - IMessageSink diagnosticMessageSink, - TestMethodDisplay defaultMethodDisplay, - TestMethodDisplayOptions defaultMethodDisplayOptions, - ITestMethod testMethod, - object[] testMethodArguments = null) - : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments) - { - _skipReason = skipReason; - } - - protected override string GetSkipReason(IAttributeInfo factAttribute) - => _skipReason ?? base.GetSkipReason(factAttribute); - - public override void Deserialize(IXunitSerializationInfo data) - { - base.Deserialize(data); - _skipReason = data.GetValue(nameof(_skipReason)); - } - - public override void Serialize(IXunitSerializationInfo data) - { - base.Serialize(data); - data.AddValue(nameof(_skipReason), _skipReason); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTestMessageBus.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTestMessageBus.cs deleted file mode 100644 index 12f8e28e74..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTestMessageBus.cs +++ /dev/null @@ -1,50 +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 Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - public class SkipTestException : Exception - { - public SkipTestException(string reason) - : base(reason) { } - } - - /// Implements message bus to communicate tests skipped via SkipTestException. - public class SkippedTestMessageBus : IMessageBus - { - readonly IMessageBus innerBus; - - public SkippedTestMessageBus(IMessageBus innerBus) - { - this.innerBus = innerBus; - } - - public int SkippedTestCount { get; private set; } - - public void Dispose() { } - - public bool QueueMessage(IMessageSinkMessage message) - { - var testFailed = message as ITestFailed; - - if (testFailed != null) - { - var exceptionType = testFailed.ExceptionTypes.FirstOrDefault(); - if (exceptionType == typeof(SkipTestException).FullName) - { - SkippedTestCount++; - return innerBus.QueueMessage(new TestSkipped(testFailed.Test, testFailed.Messages.FirstOrDefault())); - } - } - - // Nothing we care about, send it on its way - return innerBus.QueueMessage(message); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTheoryTestCase.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTheoryTestCase.cs deleted file mode 100644 index abcf828bd1..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/SkippedTheoryTestCase.cs +++ /dev/null @@ -1,39 +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.Threading; -using System.Threading.Tasks; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Microsoft.DotNet.XUnitExtensions -{ - /// Wraps RunAsync for ConditionalTheory. - public class SkippedTheoryTestCase : XunitTheoryTestCase - { - [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes", error: true)] - public SkippedTheoryTestCase() { } - - public SkippedTheoryTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod) - : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod) { } - - public override async Task RunAsync(IMessageSink diagnosticMessageSink, - IMessageBus messageBus, - object[] constructorArguments, - ExceptionAggregator aggregator, - CancellationTokenSource cancellationTokenSource) - { - SkippedTestMessageBus skipMessageBus = new SkippedTestMessageBus(messageBus); - var result = await base.RunAsync(diagnosticMessageSink, skipMessageBus, constructorArguments, aggregator, cancellationTokenSource); - if (skipMessageBus.SkippedTestCount > 0) - { - result.Failed -= skipMessageBus.SkippedTestCount; - result.Skipped += skipMessageBus.SkippedTestCount; - } - - return result; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/TargetFrameworkMonikers.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/TargetFrameworkMonikers.cs deleted file mode 100644 index 182768f741..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/TargetFrameworkMonikers.cs +++ /dev/null @@ -1,17 +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 Xunit -{ - [Flags] - public enum TargetFrameworkMonikers - { - Netcoreapp = 0x1, - NetFramework = 0x2, - Uap = 0x4, - Mono = 0x8 - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/TestPlatforms.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/TestPlatforms.cs deleted file mode 100644 index fe905803be..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/TestPlatforms.cs +++ /dev/null @@ -1,20 +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 Xunit -{ - [Flags] - public enum TestPlatforms - { - Windows = 1, - Linux = 2, - OSX = 4, - FreeBSD = 8, - NetBSD = 16, - AnyUnix = FreeBSD | Linux | NetBSD | OSX, - Any = ~0 - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/XunitConstants.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/XunitConstants.cs deleted file mode 100644 index 870342bd15..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/XunitConstants.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. - -namespace Microsoft.DotNet.XUnitExtensions -{ - public struct XunitConstants - { - internal const string NonFreeBSDTest = "nonfreebsdtests"; - internal const string NonLinuxTest = "nonlinuxtests"; - internal const string NonNetBSDTest = "nonnetbsdtests"; - internal const string NonOSXTest = "nonosxtests"; - internal const string NonWindowsTest = "nonwindowstests"; - - internal static string NonNetcoreappTest = "nonnetcoreapptests"; - internal static string NonNetfxTest = "nonnetfxtests"; - internal static string NonUapTest = "nonuaptests"; - internal static string NonMonoTest = "nonmonotests"; - - internal const string Failing = "failing"; - internal const string ActiveIssue = "activeissue"; - internal const string OuterLoop = "outerloop"; - - public const string Category = "category"; - public const string IgnoreForCI = "ignoreforci"; - public const string RequiresElevation = "requireselevation"; - } -} diff --git a/tools/GenAPI/Microsoft.Cci.Extensions/Microsoft.Cci.Extensions.csproj b/tools/GenAPI/Microsoft.Cci.Extensions/Microsoft.Cci.Extensions.csproj index 299e6c19ab..5d55385f1d 100644 --- a/tools/GenAPI/Microsoft.Cci.Extensions/Microsoft.Cci.Extensions.csproj +++ b/tools/GenAPI/Microsoft.Cci.Extensions/Microsoft.Cci.Extensions.csproj @@ -11,7 +11,7 @@ - NU1701 + $(NoWarn);NU1701 $(DefineConstants);COREFX diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 0aca13245e..b8a6172ba7 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -1,7 +1,7 @@ - 5.1.7 + 5.1.8 0 $(MdsVersionDefault).$(BuildNumber) $(AssemblyFileVersion) @@ -27,8 +27,8 @@ - 1.11.4 - 4.61.3 + 1.12.1 + 4.76.0 6.35.0 6.35.0 4.5.1 @@ -55,27 +55,31 @@ - [1.38.0,2.0.0) - [4.4.0,5.0.0) + 1.41.0 + 4.6.0 6.0.3 + 4.3.2 3.1.6 - 17.4.1 + 17.12.0 13.0.1 4.3.0 6.0.1 + 6.0.1 5.0.0 - 2.4.2 - 2.4.5 - 7.0.0-beta.22316.1 + 2.9.3 + 2.8.2 + 8.0.0-beta.25555.2 + 11.0.0-beta.25476.3 2.0.8 - 170.8.0 + 172.52.0 10.50.1600.1 0.13.2 6.0.0 6.0.1 + 1.0.3 $(NugetPackageVersion) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 7e45b87cf0..1260b66326 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -29,8 +29,13 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + + + @@ -41,8 +46,13 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + + + @@ -56,8 +66,13 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + + + @@ -73,8 +88,13 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + + + @@ -138,22 +158,20 @@ When using NuGet 3.x this package requires at least version 3.4. - - - - - - - - - - - + + + + + + + + + + - - - - + + + @@ -169,8 +187,8 @@ 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 2a18f80358..b7c06d9b80 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -26,23 +26,23 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti - + - + - + - + - + - + diff --git a/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets b/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets new file mode 100644 index 0000000000..9526d2b2bb --- /dev/null +++ b/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + diff --git a/tools/targets/NotSupported.targets b/tools/targets/NotSupported.targets index 3be0460dd1..25e45233a2 100644 --- a/tools/targets/NotSupported.targets +++ b/tools/targets/NotSupported.targets @@ -42,7 +42,7 @@ $(GenAPIArgs) -o:"$(NotSupportedSourceFile)" $(GenAPIArgs) -t:"$(GeneratePlatformNotSupportedAssemblyMessage)" $(GenAPIArgs) -global - "$(DotNetCmd) $(ToolsArtifactsDir)net6.0\Microsoft.DotNet.GenAPI.dll" + "$(DotnetPath)dotnet" "$(ToolsArtifactsDir)net6.0\Microsoft.DotNet.GenAPI.dll" "$(ToolsArtifactsDir)net472\Microsoft.DotNet.GenAPI.exe" From 211ec11cf0e4643d867d756ecb7c2ddfe79a5c05 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:57:44 -0400 Subject: [PATCH 69/76] [5.1] Updated MDS nuspec to accurately report dependencies. (#3764) --- tools/specs/Microsoft.Data.SqlClient.nuspec | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 1260b66326..274ae7b225 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -28,84 +28,84 @@ When using NuGet 3.x this package requires at least version 3.4. sqlclient microsoft.data.sqlclient - + - - - + - + + + - - - + - + - - + + + - + - + - - + + + From dd60f108567afe8b83117f34467bdc04eb25717d Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:39:32 -0400 Subject: [PATCH 70/76] - Added Azure DevOps configuration for PR approvals and code coverage. - Added GitHub code ownership configuration. --- .azuredevops/policies/approvercountpolicy.yml | 26 +++++++++++++++++++ .github/CODEOWNERS | 25 ++++++++++++++++++ azurepipelines-coverage.yml | 16 ++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 .azuredevops/policies/approvercountpolicy.yml create mode 100644 .github/CODEOWNERS create mode 100644 azurepipelines-coverage.yml diff --git a/.azuredevops/policies/approvercountpolicy.yml b/.azuredevops/policies/approvercountpolicy.yml new file mode 100644 index 0000000000..037f2877a8 --- /dev/null +++ b/.azuredevops/policies/approvercountpolicy.yml @@ -0,0 +1,26 @@ +# This file configures the Approver Count Policy Validator status check. Some +# settings here are similar to branch policies specified via the Azure DevOps UI +# that govern how PRs are approved in general. The settings here dictate how +# the validator behaves, and it can also prevent PRs from completing. +# +# https://eng.ms/docs/coreai/devdiv/one-engineering-system-1es/1es-docs/policy-service/policy-as-code/approver-count-policy-overview + +name: approver_count +description: Approver count policy for dotnet-sqlclient [internal/release/5.1] +resource: repository +where: +configuration: + approverCountPolicySettings: + isBlocking: true + requireMinimumApproverCount: 2 + creatorVoteCounts: false + allowDownvotes: false + sourcePushOptions: + resetOnSourcePush: false + requireVoteOnLastIteration: true + requireVoteOnEachIteration: false + resetRejectionsOnSourcePush: false + blockLastPusherVote: true + branchNames: + - internal/release/5.1 + displayName: dotnet-sqlclient Approver Count Policy [internal/release/5.1] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..f355b2dd5e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,25 @@ +#============================================================================== +# This is the global GitHub code owners file for the SqlClient repo. No other +# CODEOWNERS files should exist in the repo. +# +# Code owners documentation is here: +# +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# The entire repo is owned by the SqlClientDevTeam GitHub team: +# +# https://github.com/orgs/dotnet/teams/sqlclientdevteam +# +# This team must be configured with write access to the repo for code ownership +# rules to apply. +# +# The main branch should also be protected and require all PRs to be approved +# by owners. See: +# +# https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-pull-request-reviews-before-merging +# +# There should be no other owners specified in the repo. +# +* @dotnet/sqlclientdevteam + +#============================================================================== diff --git a/azurepipelines-coverage.yml b/azurepipelines-coverage.yml new file mode 100644 index 0000000000..f3434430bb --- /dev/null +++ b/azurepipelines-coverage.yml @@ -0,0 +1,16 @@ +# Azure DevOps code coverage settings: +# +# https://learn.microsoft.com/en-us/azure/devops/pipelines/test/codecoverage-for-pullrequests?view=azure-devops#configuring-coverage-settings +# +coverage: + # Code coverage status will be posted to pull requests based on targets + # defined below. + status: + # Off by default. When on, details about coverage for each file changed will + # be posted as a pull request comment. + comments: on + # Diff coverage is code coverage only for the lines changed in a pull + # request. + diff: + # Set this to a desired %. Default is 70%. + target: 70% From 039f7edf9cdc32dd7b46b8777e15fa195fbd465f Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:41:24 -0400 Subject: [PATCH 71/76] Revert "- Added Azure DevOps configuration for PR approvals and code coverage." This reverts commit dd60f108567afe8b83117f34467bdc04eb25717d. --- .azuredevops/policies/approvercountpolicy.yml | 26 ------------------- .github/CODEOWNERS | 25 ------------------ azurepipelines-coverage.yml | 16 ------------ 3 files changed, 67 deletions(-) delete mode 100644 .azuredevops/policies/approvercountpolicy.yml delete mode 100644 .github/CODEOWNERS delete mode 100644 azurepipelines-coverage.yml diff --git a/.azuredevops/policies/approvercountpolicy.yml b/.azuredevops/policies/approvercountpolicy.yml deleted file mode 100644 index 037f2877a8..0000000000 --- a/.azuredevops/policies/approvercountpolicy.yml +++ /dev/null @@ -1,26 +0,0 @@ -# This file configures the Approver Count Policy Validator status check. Some -# settings here are similar to branch policies specified via the Azure DevOps UI -# that govern how PRs are approved in general. The settings here dictate how -# the validator behaves, and it can also prevent PRs from completing. -# -# https://eng.ms/docs/coreai/devdiv/one-engineering-system-1es/1es-docs/policy-service/policy-as-code/approver-count-policy-overview - -name: approver_count -description: Approver count policy for dotnet-sqlclient [internal/release/5.1] -resource: repository -where: -configuration: - approverCountPolicySettings: - isBlocking: true - requireMinimumApproverCount: 2 - creatorVoteCounts: false - allowDownvotes: false - sourcePushOptions: - resetOnSourcePush: false - requireVoteOnLastIteration: true - requireVoteOnEachIteration: false - resetRejectionsOnSourcePush: false - blockLastPusherVote: true - branchNames: - - internal/release/5.1 - displayName: dotnet-sqlclient Approver Count Policy [internal/release/5.1] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index f355b2dd5e..0000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,25 +0,0 @@ -#============================================================================== -# This is the global GitHub code owners file for the SqlClient repo. No other -# CODEOWNERS files should exist in the repo. -# -# Code owners documentation is here: -# -# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners - -# The entire repo is owned by the SqlClientDevTeam GitHub team: -# -# https://github.com/orgs/dotnet/teams/sqlclientdevteam -# -# This team must be configured with write access to the repo for code ownership -# rules to apply. -# -# The main branch should also be protected and require all PRs to be approved -# by owners. See: -# -# https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-pull-request-reviews-before-merging -# -# There should be no other owners specified in the repo. -# -* @dotnet/sqlclientdevteam - -#============================================================================== diff --git a/azurepipelines-coverage.yml b/azurepipelines-coverage.yml deleted file mode 100644 index f3434430bb..0000000000 --- a/azurepipelines-coverage.yml +++ /dev/null @@ -1,16 +0,0 @@ -# Azure DevOps code coverage settings: -# -# https://learn.microsoft.com/en-us/azure/devops/pipelines/test/codecoverage-for-pullrequests?view=azure-devops#configuring-coverage-settings -# -coverage: - # Code coverage status will be posted to pull requests based on targets - # defined below. - status: - # Off by default. When on, details about coverage for each file changed will - # be posted as a pull request comment. - comments: on - # Diff coverage is code coverage only for the lines changed in a pull - # request. - diff: - # Set this to a desired %. Default is 70%. - target: 70% From cc5c81aeffc4d565bc7f4194f7f9dab0596cda4c Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 20 Nov 2025 16:30:13 -0400 Subject: [PATCH 72/76] Reapply "- Added Azure DevOps configuration for PR approvals and code coverage." (#3785) --- .azuredevops/policies/approvercountpolicy.yml | 26 +++++++++++++++++++ .github/CODEOWNERS | 25 ++++++++++++++++++ azurepipelines-coverage.yml | 16 ++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 .azuredevops/policies/approvercountpolicy.yml create mode 100644 .github/CODEOWNERS create mode 100644 azurepipelines-coverage.yml diff --git a/.azuredevops/policies/approvercountpolicy.yml b/.azuredevops/policies/approvercountpolicy.yml new file mode 100644 index 0000000000..037f2877a8 --- /dev/null +++ b/.azuredevops/policies/approvercountpolicy.yml @@ -0,0 +1,26 @@ +# This file configures the Approver Count Policy Validator status check. Some +# settings here are similar to branch policies specified via the Azure DevOps UI +# that govern how PRs are approved in general. The settings here dictate how +# the validator behaves, and it can also prevent PRs from completing. +# +# https://eng.ms/docs/coreai/devdiv/one-engineering-system-1es/1es-docs/policy-service/policy-as-code/approver-count-policy-overview + +name: approver_count +description: Approver count policy for dotnet-sqlclient [internal/release/5.1] +resource: repository +where: +configuration: + approverCountPolicySettings: + isBlocking: true + requireMinimumApproverCount: 2 + creatorVoteCounts: false + allowDownvotes: false + sourcePushOptions: + resetOnSourcePush: false + requireVoteOnLastIteration: true + requireVoteOnEachIteration: false + resetRejectionsOnSourcePush: false + blockLastPusherVote: true + branchNames: + - internal/release/5.1 + displayName: dotnet-sqlclient Approver Count Policy [internal/release/5.1] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..f355b2dd5e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,25 @@ +#============================================================================== +# This is the global GitHub code owners file for the SqlClient repo. No other +# CODEOWNERS files should exist in the repo. +# +# Code owners documentation is here: +# +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# The entire repo is owned by the SqlClientDevTeam GitHub team: +# +# https://github.com/orgs/dotnet/teams/sqlclientdevteam +# +# This team must be configured with write access to the repo for code ownership +# rules to apply. +# +# The main branch should also be protected and require all PRs to be approved +# by owners. See: +# +# https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-pull-request-reviews-before-merging +# +# There should be no other owners specified in the repo. +# +* @dotnet/sqlclientdevteam + +#============================================================================== diff --git a/azurepipelines-coverage.yml b/azurepipelines-coverage.yml new file mode 100644 index 0000000000..f3434430bb --- /dev/null +++ b/azurepipelines-coverage.yml @@ -0,0 +1,16 @@ +# Azure DevOps code coverage settings: +# +# https://learn.microsoft.com/en-us/azure/devops/pipelines/test/codecoverage-for-pullrequests?view=azure-devops#configuring-coverage-settings +# +coverage: + # Code coverage status will be posted to pull requests based on targets + # defined below. + status: + # Off by default. When on, details about coverage for each file changed will + # be posted as a pull request comment. + comments: on + # Diff coverage is code coverage only for the lines changed in a pull + # request. + diff: + # Set this to a desired %. Default is 70%. + target: 70% From 69a924e8e12059d7cb9d34f9c33ca7f7c5df5759 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:34:09 -0800 Subject: [PATCH 73/76] [5.1] Optimization: Use Environment.TickCount for SqlStatistics execution timing (#3831) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Yusuf Mohammed <67040622+SAY14489@users.noreply.github.com> --- .../src/Microsoft/Data/Common/AdapterUtil.cs | 7 ++ .../Microsoft/Data/SqlClient/SqlStatistics.cs | 21 +++--- .../Microsoft.Data.SqlClient.Tests.csproj | 1 + .../FunctionalTests/TickCountElapsedTest.cs | 70 +++++++++++++++++++ .../SQL/SqlBulkCopyTest/CopyAllFromReader.cs | 2 - 5 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/FunctionalTests/TickCountElapsedTest.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index 8f48131f8d..145681f8df 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -582,6 +582,13 @@ internal static Delegate FindBuilder(MulticastDelegate mcd) internal static long TimerCurrent() => DateTime.UtcNow.ToFileTimeUtc(); + internal static long FastTimerCurrent() => Environment.TickCount; + + internal static uint CalculateTickCountElapsed(long startTick, long endTick) + { + return (uint)(endTick - startTick); + } + internal static long TimerFromSeconds(int seconds) { long result = checked((long)seconds * TimeSpan.TicksPerSecond); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs index 7a5e37198b..026a437cb7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs @@ -38,7 +38,7 @@ internal static ValueSqlStatisticsScope TimedScope(SqlStatistics statistics) // internal values that are not exposed through properties internal long _closeTimestamp; internal long _openTimestamp; - internal long _startExecutionTimestamp; + internal long? _startExecutionTimestamp; internal long _startFetchTimestamp; internal long _startNetworkServerTimestamp; @@ -80,7 +80,7 @@ internal bool WaitForDoneAfterRow internal void ContinueOnNewConnection() { - _startExecutionTimestamp = 0; + _startExecutionTimestamp = null; _startFetchTimestamp = 0; _waitForDoneAfterRow = false; _waitForReply = false; @@ -108,7 +108,7 @@ internal IDictionary GetDictionary() { "UnpreparedExecs", _unpreparedExecs }, { "ConnectionTime", ADP.TimerToMilliseconds(_connectionTime) }, - { "ExecutionTime", ADP.TimerToMilliseconds(_executionTime) }, + { "ExecutionTime", _executionTime }, { "NetworkServerTime", ADP.TimerToMilliseconds(_networkServerTime) } }; Debug.Assert(dictionary.Count == Count); @@ -117,9 +117,9 @@ internal IDictionary GetDictionary() internal bool RequestExecutionTimer() { - if (_startExecutionTimestamp == 0) + if (!_startExecutionTimestamp.HasValue) { - _startExecutionTimestamp = ADP.TimerCurrent(); + _startExecutionTimestamp = ADP.FastTimerCurrent(); return true; } return false; @@ -127,7 +127,7 @@ internal bool RequestExecutionTimer() internal void RequestNetworkServerTimer() { - Debug.Assert(_startExecutionTimestamp != 0, "No network time expected outside execution period"); + Debug.Assert(_startExecutionTimestamp.HasValue, "No network time expected outside execution period"); if (_startNetworkServerTimestamp == 0) { _startNetworkServerTimestamp = ADP.TimerCurrent(); @@ -137,10 +137,11 @@ internal void RequestNetworkServerTimer() internal void ReleaseAndUpdateExecutionTimer() { - if (_startExecutionTimestamp > 0) + if (_startExecutionTimestamp.HasValue) { - _executionTime += (ADP.TimerCurrent() - _startExecutionTimestamp); - _startExecutionTimestamp = 0; + uint elapsed = ADP.CalculateTickCountElapsed(_startExecutionTimestamp.Value, ADP.FastTimerCurrent()); + _executionTime += elapsed; + _startExecutionTimestamp = null; } } @@ -176,7 +177,7 @@ internal void Reset() _unpreparedExecs = 0; _waitForDoneAfterRow = false; _waitForReply = false; - _startExecutionTimestamp = 0; + _startExecutionTimestamp = null; _startNetworkServerTimestamp = 0; } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index 2336768391..89efdfc9be 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -62,6 +62,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TickCountElapsedTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TickCountElapsedTest.cs new file mode 100644 index 0000000000..33f5c660eb --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TickCountElapsedTest.cs @@ -0,0 +1,70 @@ +// 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.Reflection; +using Microsoft.Data.Common; +using Xunit; + +namespace Microsoft.Data.SqlClient.Tests +{ + + /// + /// Tests for Environment.TickCount elapsed time calculation with wraparound handling. + /// + public sealed class TickCountElapsedTest + { + /// + /// Invokes the internal CalculateTickCountElapsed method to compute elapsed time between two tick counts. + /// + /// + /// + /// + internal static uint CalculateTickCountElapsed(long startTick, long endTick) { + var adpType = Assembly.GetAssembly(typeof(SqlConnection)).GetType("Microsoft.Data.Common.ADP"); + return (uint) adpType.GetMethod("CalculateTickCountElapsed", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] {startTick, endTick}); + } + + /// + /// Verifies that normal elapsed time calculation works correctly. + /// + [Fact] + public void CalculateTickCountElapsed_NormalCase_ReturnsCorrectElapsed() + { + uint elapsed = CalculateTickCountElapsed(1000, 1500); + Assert.Equal(500u, elapsed); + } + + /// + /// Verifies that wraparound from int.MaxValue to int.MinValue is handled correctly. + /// + [Fact] + public void CalculateTickCountElapsed_MaxWraparound_ReturnsOne() + { + uint elapsed = CalculateTickCountElapsed(int.MaxValue, int.MinValue); + Assert.Equal(1u, elapsed); + } + + /// + /// Verifies that partial wraparound scenarios work correctly. + /// + [Theory] + [InlineData(2147483600, -2147483600, 96u)] + [InlineData(2147483647, -2147483647, 2u)] + public void CalculateTickCountElapsed_PartialWraparound_ReturnsCorrectElapsed(long start, long end, uint expected) + { + uint elapsed = CalculateTickCountElapsed(start, end); + Assert.Equal(expected, elapsed); + } + + /// + /// Verifies that zero elapsed time returns zero. + /// + [Fact] + public void CalculateTickCountElapsed_ZeroElapsed_ReturnsZero() + { + uint elapsed = CalculateTickCountElapsed(1000, 1000); + Assert.Equal(0u, elapsed); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReader.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReader.cs index 4d1dd14cfb..beb8df7992 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReader.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReader.cs @@ -52,8 +52,6 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) Assert.True(0 < (long)stats["BytesReceived"], "BytesReceived is non-positive."); Assert.True(0 < (long)stats["BytesSent"], "BytesSent is non-positive."); - Assert.True((long)stats["ConnectionTime"] >= (long)stats["ExecutionTime"], "Connection Time is less than Execution Time."); - Assert.True((long)stats["ExecutionTime"] >= (long)stats["NetworkServerTime"], "Execution Time is less than Network Server Time."); DataTestUtility.AssertEqualsWithDescription((long)0, (long)stats["UnpreparedExecs"], "Non-zero UnpreparedExecs value: " + (long)stats["UnpreparedExecs"]); DataTestUtility.AssertEqualsWithDescription((long)0, (long)stats["PreparedExecs"], "Non-zero PreparedExecs value: " + (long)stats["PreparedExecs"]); DataTestUtility.AssertEqualsWithDescription((long)0, (long)stats["Prepares"], "Non-zero Prepares value: " + (long)stats["Prepares"]); From d3aea3e529fd683d1af51db2d228f83ffdb35d94 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:57:09 -0400 Subject: [PATCH 74/76] [5.1] Dependency Cleanup (#3838) --- .github/workflows/codeql.yml | 94 +++++++++++++++++++ eng/pipelines/libraries/mds-variables.yml | 2 +- global.json | 5 + ...waysEncrypted.AzureKeyVaultProvider.csproj | 1 - .../ref/Microsoft.Data.SqlClient.csproj | 7 +- .../src/Microsoft.Data.SqlClient.csproj | 9 +- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 3 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 +- ...nectionString.cs => DbConnectionString.cs} | 0 .../Microsoft/Data/SqlClient/SqlConnection.cs | 2 +- .../GenerateResourceStringsSource.targets | 17 ++-- ...tadataFactory.cs => SqlMetaDataFactory.cs} | 0 .../Microsoft.Data.SqlClient.Tests.csproj | 4 - ....Data.SqlClient.ManualTesting.Tests.csproj | 3 - .../SQL/UdtTest/UDTs/Address/Address.csproj | 1 - .../SQL/UdtTest/UDTs/Circle/Circle.csproj | 1 - .../SQL/UdtTest/UDTs/Shapes/Shapes.csproj | 1 - .../UdtTest/UDTs/Utf8String/Utf8String.csproj | 1 - ...crosoft.Data.SqlClient.ExtUtilities.csproj | 8 +- ...rosoft.Data.SqlClient.TestUtilities.csproj | 2 - .../TDS/TDS.EndPoint/TDSServerEndPoint.cs | 29 ++++-- .../TDSServerEndPointConnection.cs | 11 ++- tools/props/Versions.props | 62 ++++++------ tools/specs/Microsoft.Data.SqlClient.nuspec | 16 ++-- ...waysEncrypted.AzureKeyVaultProvider.nuspec | 3 - 25 files changed, 194 insertions(+), 91 deletions(-) create mode 100644 .github/workflows/codeql.yml create mode 100644 global.json rename src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/{DBConnectionString.cs => DbConnectionString.cs} (100%) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/{SqlMetadataFactory.cs => SqlMetaDataFactory.cs} (100%) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..51f6558aac --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,94 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "release/5.1" ] + pull_request: + branches: [ "release/5.1" ] + schedule: + - cron: '33 23 * * 6' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: csharp + build-mode: manual + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET Core SDK + uses: actions/setup-dotnet@v5.0.1 + with: + global-json-file: global.json + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - name: Run manual build steps + if: matrix.build-mode == 'manual' + shell: bash + run: | + mkdir packages + dotnet build src/Microsoft.Data.SqlClient.sln + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/eng/pipelines/libraries/mds-variables.yml b/eng/pipelines/libraries/mds-variables.yml index e32c298ae8..01f605dd5b 100644 --- a/eng/pipelines/libraries/mds-variables.yml +++ b/eng/pipelines/libraries/mds-variables.yml @@ -21,7 +21,7 @@ variables: - name: LocalMdsMinor value: 1 - name: LocalMdsPatch - value: 8 + value: 9 - name: Packaging.EnableSBOMSigning value: true diff --git a/global.json b/global.json new file mode 100644 index 0000000000..68bd8fe2d3 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "8.0.416" + } +} 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 d68e3ee661..319716a669 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 @@ -32,7 +32,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index 7ef9ff4f78..8a8390db69 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -42,18 +42,12 @@ - - - - - - - - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DBConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionString.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DBConnectionString.cs rename to src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionString.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 04acfcb612..af2099e6d0 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 @@ -2998,7 +2998,7 @@ internal byte[] GetBytes(object o, out Format format, out int maxSize) // You must not change the guid for this coclass // or the iid for the ISQLDebug interface // - /// + /// [ ComVisible(true), ClassInterface(ClassInterfaceType.None), diff --git a/src/Microsoft.Data.SqlClient/netfx/tools/targets/GenerateResourceStringsSource.targets b/src/Microsoft.Data.SqlClient/netfx/tools/targets/GenerateResourceStringsSource.targets index eb2be2ae9d..1c8bf89865 100644 --- a/src/Microsoft.Data.SqlClient/netfx/tools/targets/GenerateResourceStringsSource.targets +++ b/src/Microsoft.Data.SqlClient/netfx/tools/targets/GenerateResourceStringsSource.targets @@ -1,10 +1,15 @@ - - - - - - + + pwsh + powershell.exe + + + + + + + + diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetadataFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetadataFactory.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index 89efdfc9be..5b9efac4e8 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -86,11 +86,8 @@ - - - @@ -115,7 +112,6 @@ - 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 95a4dd8723..ea59717ef3 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 @@ -323,13 +323,11 @@ - - @@ -338,7 +336,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj index e525bdeda3..4bd155fafe 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj index 750ae10973..f18edeec5e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj index d2b894a83d..4e321166ff 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj index 4b520c0286..4c9c790bbb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/Microsoft.Data.SqlClient.ExtUtilities.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/Microsoft.Data.SqlClient.ExtUtilities.csproj index 7219ca2c8b..5306ece191 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/Microsoft.Data.SqlClient.ExtUtilities.csproj +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/Microsoft.Data.SqlClient.ExtUtilities.csproj @@ -7,7 +7,13 @@ - + + diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj index 1ca32f6754..7929f589a1 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj @@ -14,8 +14,6 @@ PreserveNewest - - diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerEndPoint.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerEndPoint.cs index e81139c63a..48e2a46d89 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerEndPoint.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerEndPoint.cs @@ -100,7 +100,7 @@ public void Start() // Update ServerEndpoint with the actual address/port, e.g. if port=0 was given ServerEndPoint = (IPEndPoint)ListenerSocket.LocalEndpoint; - Log($"{GetType().Name} {EndpointName} Is Server Socket Bound: {ListenerSocket.Server.IsBound} Testing connectivity to the endpoint created for the server."); + Log($"Is Server Socket Bound: {ListenerSocket.Server.IsBound} Testing connectivity to the endpoint created for the server."); using (TcpClient client = new TcpClient()) { try @@ -109,18 +109,18 @@ public void Start() } catch (Exception e) { - Log($"{GetType().Name} {EndpointName} Error occurred while testing server endpoint {e.Message}"); + Log($"Error occurred while testing server endpoint {e.Message}"); throw; } } - Log($"{GetType().Name} {EndpointName} Endpoint test successful."); + Log("Endpoint test successful."); // Initialize the listener ListenerThread = new Thread(new ThreadStart(_RequestListener)) { IsBackground = true }; ListenerThread.Name = "TDS Server EndPoint Listener"; ListenerThread.Start(); - Log($"{GetType().Name} {EndpointName} Listener Thread Started "); + Log("Listener Thread Started "); } /// @@ -203,6 +203,8 @@ private void _RequestListener() // Register a new connection Connections.Add(connection); } + + Log($"New connection accepted: {connection.RemoteEndPoint} Total connections: {Connections.Count} "); } catch (Exception ex) { @@ -232,23 +234,34 @@ private void _RequestListener() /// private void _OnConnectionClosed(object sender, EventArgs e) { + T clientConnection = sender as T; + bool removed = false; + // Synchronize access to connection collection lock (Connections) { // Remove the existing connection from the list - Connections.Remove(sender as T); - Log($"{GetType().Name} {EndpointName} Connection Closed"); + removed = Connections.Remove(clientConnection); + } + + if (removed) + { + Log($"Connection closed and removed: {clientConnection.RemoteEndPoint}"); + } + else + { + Log($"Connection closed but NOT removed (not found): {clientConnection.RemoteEndPoint}"); } } /// /// Write a string to the log /// - internal void Log(string text, params object[] args) + internal void Log(string text) { if (EventLog != null) { - EventLog.WriteLine(text, args); + EventLog.WriteLine($"{GetType().Name} {EndpointName} {ServerEndPoint} {text}"); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerEndPointConnection.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerEndPointConnection.cs index 6327189691..cd8a1c49d6 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerEndPointConnection.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerEndPointConnection.cs @@ -74,7 +74,13 @@ public abstract class ServerEndPointConnection /// /// Connection itself /// - protected TcpClient Connection { get; set; } + public TcpClient Connection { get; set; } + + /// + /// We copy the remote endpoint so it can be used _after_ the connection + /// is closed. + /// + public System.Net.EndPoint RemoteEndPoint { get; } /// /// The flag indicates whether server is being stopped @@ -89,8 +95,9 @@ public ServerEndPointConnection(ITDSServer server, TcpClient connection) // Save server Server = server; - // Save TCP connection + // Save TCP connection and its remote endpoint. Connection = connection; + RemoteEndPoint = connection.Client.RemoteEndPoint; // Configure timeouts Connection.ReceiveTimeout = 1000; diff --git a/tools/props/Versions.props b/tools/props/Versions.props index b8a6172ba7..b73c99bfa7 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -1,5 +1,7 @@ + + 5.1.8 0 @@ -12,6 +14,8 @@ $(MdsVersionDefault)-dev $(NugetPackageVersion) + + @@ -21,68 +25,68 @@ 1.0.0-dev $(SqlServerPackageVersion) - + + - 5.1.2 + $(NugetPackageVersion) + $(NugetPackageVersion) - + + + 1.41.0 1.12.1 4.76.0 - 6.35.0 6.35.0 - 4.5.1 - 6.0.1 - 6.0.11 + 6.35.0 + 4.6.1 + 6.35.0 - + + + + 5.1.2 + + + - 5.0.0 5.1.2 - 6.0.1 1.0.0 - 6.0.1 - 6.0.1 + 6.0.1 6.0.0 - 5.0.0 6.0.0 5.0.0 6.0.0 - + + + 5.0.0 4.3.0 + 5.0.0 + - 1.41.0 4.6.0 6.0.3 4.3.2 - + + - 3.1.6 17.12.0 - 13.0.1 - 4.3.0 - 6.0.1 - 6.0.1 - 5.0.0 + 13.0.4 + 6.0.2 2.9.3 2.8.2 8.0.0-beta.25555.2 11.0.0-beta.25476.3 - 2.0.8 172.52.0 10.50.1600.1 - 0.13.2 + 0.14.0 6.0.0 - 6.0.1 + 8.0.1 1.0.3 - - $(NugetPackageVersion) - $(NugetPackageVersion) - diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 274ae7b225..ca48e4d915 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -38,9 +38,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - - + +