From 58377f5885c0d1899d40ffcaa28151b9c6bd937d Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:53:44 +0100 Subject: [PATCH 01/18] Align usings --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 8079231f61..8d2cf2fb96 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 @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Data; using System.Data.Common; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -15,12 +14,11 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Transactions; using Microsoft.Data.Common; using Microsoft.Data.ProviderBase; using Microsoft.Data.SqlClient.ConnectionPool; using Microsoft.Identity.Client; -using System.Transactions; - namespace Microsoft.Data.SqlClient { From e069ce2741fd0c1d2ad7e6f0b3362df6cc8d7684 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:54:54 +0100 Subject: [PATCH 02/18] netfx: seal classes --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8d2cf2fb96..9467c2d249 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 @@ -22,7 +22,7 @@ namespace Microsoft.Data.SqlClient { - internal class SessionStateRecord + internal sealed class SessionStateRecord { internal bool _recoverable; internal UInt32 _version; @@ -30,7 +30,7 @@ internal class SessionStateRecord internal byte[] _data; } - internal class SessionData + internal sealed class SessionData { internal const int _maxNumberOfSessionStates = 256; internal UInt32 _tdsVersion; From 4746d351ae0e8f528659b075078a255715aca9c7 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:57:39 +0100 Subject: [PATCH 03/18] netfx: sync variable data types: UInt32/Int32 to uint/int --- .../Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 9467c2d249..239eb0c511 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 @@ -25,15 +25,15 @@ namespace Microsoft.Data.SqlClient internal sealed class SessionStateRecord { internal bool _recoverable; - internal UInt32 _version; - internal Int32 _dataLength; + internal uint _version; + internal int _dataLength; internal byte[] _data; } internal sealed class SessionData { internal const int _maxNumberOfSessionStates = 256; - internal UInt32 _tdsVersion; + internal uint _tdsVersion; internal bool _encrypted; internal string _database; From d7fc3ed997441034218c749393f20d54f0688edc Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 00:04:17 +0100 Subject: [PATCH 04/18] netfx: reorder accessibility modifiers --- .../SqlClient/SqlInternalConnectionTds.cs | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) 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 239eb0c511..2a7c289a5f 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 @@ -102,7 +102,7 @@ public void AssertUnrecoverableStateCountIsCorrect() } } - sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposable + internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposable { // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600 internal const int MsalHttpRetryStatusCode = 429; @@ -299,7 +299,7 @@ internal SessionData CurrentSessionData // 6. Reading ThreadHasParserLockForClose is thread-safe internal class SyncAsyncLock { - SemaphoreSlim semaphore = new SemaphoreSlim(1); + private SemaphoreSlim semaphore = new SemaphoreSlim(1); internal void Wait(bool canReleaseFromAnyThread) { @@ -656,7 +656,7 @@ internal RoutingInfo RoutingInfo } } - override internal SqlInternalTransaction CurrentTransaction + internal override SqlInternalTransaction CurrentTransaction { get { @@ -664,7 +664,7 @@ override internal SqlInternalTransaction CurrentTransaction } } - override internal SqlInternalTransaction AvailableInternalTransaction + internal override SqlInternalTransaction AvailableInternalTransaction { get { @@ -672,8 +672,7 @@ override internal SqlInternalTransaction AvailableInternalTransaction } } - - override internal SqlInternalTransaction PendingTransaction + internal override SqlInternalTransaction PendingTransaction { get { @@ -697,7 +696,7 @@ internal string InstanceName } } - override internal bool IsLockedForBulkCopy + internal override bool IsLockedForBulkCopy { get { @@ -705,7 +704,7 @@ override internal bool IsLockedForBulkCopy } } - override protected internal bool IsNonPoolableTransactionRoot + internal protected override bool IsNonPoolableTransactionRoot { get { @@ -713,7 +712,7 @@ override protected internal bool IsNonPoolableTransactionRoot } } - override internal bool Is2008OrNewer + internal override bool Is2008OrNewer { get { @@ -753,7 +752,7 @@ internal SqlConnectionPoolGroupProviderInfo PoolGroupProviderInfo } } - override protected bool ReadyToPrepareTransaction + protected override bool ReadyToPrepareTransaction { get { @@ -763,7 +762,7 @@ override protected bool ReadyToPrepareTransaction } } - override public string ServerVersion + public override string ServerVersion { get { @@ -771,6 +770,7 @@ override public string ServerVersion (short)_loginAck.minorVersion, _loginAck.buildNum)); } } + public int ServerProcessId { get @@ -806,7 +806,7 @@ protected override bool UnbindOnTransactionCompletion // GENERAL METHODS //////////////////////////////////////////////////////////////////////////////////////// [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // copied from Triaged.cs - override protected void ChangeDatabaseInternal(string database) + protected override void ChangeDatabaseInternal(string database) { // MDAC 73598 - add brackets around database database = SqlConnection.FixupDatabaseTransactionName(database); @@ -815,7 +815,7 @@ override protected void ChangeDatabaseInternal(string database) _parser.Run(RunBehavior.UntilDone, null, null, null, _parser._physicalStateObj); } - override public void Dispose() + public override void Dispose() { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} disposing", ObjectID); try @@ -838,7 +838,7 @@ override public void Dispose() base.Dispose(); } - override internal void ValidateConnectionForExecute(SqlCommand command) + internal override void ValidateConnectionForExecute(SqlCommand command) { TdsParser parser = _parser; if ((parser == null) || (parser.State == TdsParserState.Broken) || (parser.State == TdsParserState.Closed)) @@ -946,7 +946,7 @@ internal override bool IsConnectionAlive(bool throwOnException) => // POOLING METHODS //////////////////////////////////////////////////////////////////////////////////////// - override protected void Activate(Transaction transaction) + protected override void Activate(Transaction transaction) { FailoverPermissionDemand(); // Demand for unspecified failover pooled connections @@ -971,7 +971,7 @@ override protected void Activate(Transaction transaction) } } - override protected void InternalDeactivate() + protected override void InternalDeactivate() { // When we're deactivated, the user must have called End on all // the async commands, or we don't know that we're in a state that @@ -1044,7 +1044,7 @@ internal void IncrementAsyncCount() // LOCAL TRANSACTION METHODS //////////////////////////////////////////////////////////////////////////////////////// - override internal void DisconnectTransaction(SqlInternalTransaction internalTransaction) + internal override void DisconnectTransaction(SqlInternalTransaction internalTransaction) { TdsParser parser = Parser; @@ -1059,7 +1059,7 @@ internal void ExecuteTransaction(TransactionRequest transactionRequest, string n ExecuteTransaction(transactionRequest, name, iso, null, false); } - override internal void ExecuteTransaction(TransactionRequest transactionRequest, string name, System.Data.IsolationLevel iso, SqlInternalTransaction internalTransaction, bool isDelegateControlRequest) + internal override void ExecuteTransaction(TransactionRequest transactionRequest, string name, System.Data.IsolationLevel iso, SqlInternalTransaction internalTransaction, bool isDelegateControlRequest) { if (IsConnectionDoomed) { // doomed means we can't do anything else... @@ -1239,20 +1239,20 @@ internal void ExecuteTransaction2005( // DISTRIBUTED TRANSACTION METHODS //////////////////////////////////////////////////////////////////////////////////////// - override internal void DelegatedTransactionEnded() + internal override void DelegatedTransactionEnded() { // TODO: I don't like the way that this works, but I don't want to rototill the entire pooler to avoid this call. base.DelegatedTransactionEnded(); } - override protected byte[] GetDTCAddress() + protected override byte[] GetDTCAddress() { byte[] dtcAddress = _parser.GetDTCAddress(ConnectionOptions.ConnectTimeout, _parser.GetSession(this)); Debug.Assert(dtcAddress != null, "null dtcAddress?"); return dtcAddress; } - override protected void PropagateTransactionCookie(byte[] cookie) + protected override void PropagateTransactionCookie(byte[] cookie) { _parser.PropagateDistributedTransaction(cookie, ConnectionOptions.ConnectTimeout, _parser._physicalStateObj); } From cf1898f473c691757687801626627691fdfd2657 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 00:09:08 +0100 Subject: [PATCH 05/18] netfx: reorder variables --- .../Data/SqlClient/SqlInternalConnectionTds.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) 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 2a7c289a5f..1901b8a527 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 @@ -107,12 +107,13 @@ internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposa // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600 internal const int MsalHttpRetryStatusCode = 429; - // Connection re-route limit - internal const int _maxNumberOfRedirectRoute = 10; - // CONNECTION AND STATE VARIABLES private readonly SqlConnectionPoolGroupProviderInfo _poolGroupProviderInfo; // will only be null when called for ChangePassword, or creating SSE User Instance private TdsParser _parser; + + // Connection re-route limit + internal const int _maxNumberOfRedirectRoute = 10; + private SqlLoginAck _loginAck; private SqlCredential _credential; private FederatedAuthenticationFeatureExtensionData _fedAuthFeatureExtensionData; @@ -126,7 +127,6 @@ internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposa // Federated Authentication // Response obtained from the server for FEDAUTHREQUIRED prelogin option. internal bool _fedAuthRequired; - internal bool _federatedAuthenticationRequested; internal bool _federatedAuthenticationAcknowledged; internal bool _federatedAuthenticationInfoRequested; // Keep this distinct from _federatedAuthenticationRequested, since some fedauth library types may not need more info @@ -135,7 +135,7 @@ internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposa // The Federated Authentication returned by TryGetFedAuthTokenLocked or GetFedAuthToken. SqlFedAuthToken _fedAuthToken = null; internal byte[] _accessTokenInBytes; - internal readonly Func> _accessTokenCallback; + internal readonly Func> _accessTokenCallback; private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper; @@ -251,8 +251,6 @@ internal bool IsDNSCachingBeforeRedirectSupported // If the context is expiring within the next 10 mins, then create a new context, irrespective of if another thread is trying to do the same. private static readonly TimeSpan _dbAuthenticationContextUnLockedRefreshTimeSpan = new TimeSpan(hours: 0, minutes: 10, seconds: 00); - private readonly TimeoutTimer _timeout; - private static HashSet transientErrors = new HashSet(); internal SessionData CurrentSessionData @@ -405,6 +403,7 @@ internal SqlConnectionTimeoutErrorInternal TimeoutErrorInternal RoutingInfo _routingInfo = null; private Guid _originalClientConnectionId = Guid.Empty; private string _routingDestination = null; + private readonly TimeoutTimer _timeout; static SqlInternalConnectionTds() { From 7b13a660fc3b8a340b95197797028fbbec3b5511 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 00:19:49 +0100 Subject: [PATCH 06/18] netfx, netcore: synced variable names --- .../SqlClient/SqlInternalConnectionTds.cs | 6 +- .../SqlClient/SqlInternalConnectionTds.cs | 74 +++++++++---------- 2 files changed, 40 insertions(+), 40 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 d128268185..320a878fc9 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 @@ -1817,14 +1817,14 @@ TimeoutTimer timeout intervalTimer, withFailover: true ); - int routingAttemps = 0; + int routingAttempts = 0; while (RoutingInfo != null) { - if (routingAttemps > _maxNumberOfRedirectRoute) + if (routingAttempts > _maxNumberOfRedirectRoute) { throw SQL.ROR_RecursiveRoutingNotSupported(this, _maxNumberOfRedirectRoute); } - routingAttemps++; + routingAttempts++; SqlClientEventSource.Log.TryTraceEvent(" Routed to {0}", RoutingInfo.ServerName); 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 1901b8a527..8b69d2ae05 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 @@ -186,7 +186,7 @@ internal bool IsSQLDNSRetryEnabled } } - private bool DNSCachingBeforeRedirect = false; + private bool _DNSCachingBeforeRedirect = false; /// /// Get or set if the control ring send redirect token and SQLDNSCaching FeatureExtAck with true @@ -195,11 +195,11 @@ internal bool IsDNSCachingBeforeRedirectSupported { get { - return DNSCachingBeforeRedirect; + return _DNSCachingBeforeRedirect; } set { - DNSCachingBeforeRedirect = value; + _DNSCachingBeforeRedirect = value; } } @@ -251,7 +251,7 @@ internal bool IsDNSCachingBeforeRedirectSupported // If the context is expiring within the next 10 mins, then create a new context, irrespective of if another thread is trying to do the same. private static readonly TimeSpan _dbAuthenticationContextUnLockedRefreshTimeSpan = new TimeSpan(hours: 0, minutes: 10, seconds: 00); - private static HashSet transientErrors = new HashSet(); + private static HashSet s_transientErrors = new HashSet(); internal SessionData CurrentSessionData { @@ -297,21 +297,21 @@ internal SessionData CurrentSessionData // 6. Reading ThreadHasParserLockForClose is thread-safe internal class SyncAsyncLock { - private SemaphoreSlim semaphore = new SemaphoreSlim(1); + private SemaphoreSlim _semaphore = new SemaphoreSlim(1); internal void Wait(bool canReleaseFromAnyThread) { - Monitor.Enter(semaphore); // semaphore is used as lock object, no relation to SemaphoreSlim.Wait/Release methods - if (canReleaseFromAnyThread || semaphore.CurrentCount == 0) + Monitor.Enter(_semaphore); // semaphore is used as lock object, no relation to SemaphoreSlim.Wait/Release methods + if (canReleaseFromAnyThread || _semaphore.CurrentCount == 0) { - semaphore.Wait(); + _semaphore.Wait(); if (canReleaseFromAnyThread) { - Monitor.Exit(semaphore); + Monitor.Exit(_semaphore); } else { - semaphore.Release(); + _semaphore.Release(); } } } @@ -322,21 +322,21 @@ internal void Wait(bool canReleaseFromAnyThread, int timeout, ref bool lockTaken bool hasMonitor = false; try { - Monitor.TryEnter(semaphore, timeout, ref hasMonitor); // semaphore is used as lock object, no relation to SemaphoreSlim.Wait/Release methods + Monitor.TryEnter(_semaphore, timeout, ref hasMonitor); // semaphore is used as lock object, no relation to SemaphoreSlim.Wait/Release methods if (hasMonitor) { - if ((canReleaseFromAnyThread) || (semaphore.CurrentCount == 0)) + if ((canReleaseFromAnyThread) || (_semaphore.CurrentCount == 0)) { - if (semaphore.Wait(timeout)) + if (_semaphore.Wait(timeout)) { if (canReleaseFromAnyThread) { - Monitor.Exit(semaphore); + Monitor.Exit(_semaphore); hasMonitor = false; } else { - semaphore.Release(); + _semaphore.Release(); } lockTaken = true; } @@ -351,20 +351,20 @@ internal void Wait(bool canReleaseFromAnyThread, int timeout, ref bool lockTaken { if ((!lockTaken) && (hasMonitor)) { - Monitor.Exit(semaphore); + Monitor.Exit(_semaphore); } } } internal void Release() { - if (semaphore.CurrentCount == 0) + if (_semaphore.CurrentCount == 0) { // semaphore methods were used for locking - semaphore.Release(); + _semaphore.Release(); } else { - Monitor.Exit(semaphore); + Monitor.Exit(_semaphore); } } @@ -373,14 +373,14 @@ internal bool CanBeReleasedFromAnyThread { get { - return semaphore.CurrentCount == 0; + return _semaphore.CurrentCount == 0; } } // Necessary but not sufficient condition for thread to have lock (since sempahore may be obtained by any thread) internal bool ThreadMayHaveLock() { - return Monitor.IsEntered(semaphore) || semaphore.CurrentCount == 0; + return Monitor.IsEntered(_semaphore) || _semaphore.CurrentCount == 0; } } @@ -567,36 +567,36 @@ private static void populateTransientErrors() { // SQL Error Code: 4060 // Cannot open database "%.*ls" requested by the login. The login failed. - transientErrors.Add(4060); + s_transientErrors.Add(4060); // SQL Error Code: 10928 // Resource ID: %d. The %s limit for the database is %d and has been reached. - transientErrors.Add(10928); + s_transientErrors.Add(10928); // SQL Error Code: 10929 // Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. // However, the server is currently too busy to support requests greater than %d for this database. - transientErrors.Add(10929); + s_transientErrors.Add(10929); // SQL Error Code: 40197 // You will receive this error, when the service is down due to software or hardware upgrades, hardware failures, // or any other failover problems. The error code (%d) embedded within the message of error 40197 provides // additional information about the kind of failure or failover that occurred. Some examples of the error codes are // embedded within the message of error 40197 are 40020, 40143, 40166, and 40540. - transientErrors.Add(40197); - transientErrors.Add(40020); - transientErrors.Add(40143); - transientErrors.Add(40166); + s_transientErrors.Add(40197); + s_transientErrors.Add(40020); + s_transientErrors.Add(40143); + s_transientErrors.Add(40166); // The service has encountered an error processing your request. Please try again. - transientErrors.Add(40540); + s_transientErrors.Add(40540); // The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. - transientErrors.Add(40501); + s_transientErrors.Add(40501); // Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. // If the problem persists, contact customer support, and provide them the session tracing ID of '%.*ls'. - transientErrors.Add(40613); + s_transientErrors.Add(40613); // Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. - transientErrors.Add(42108); + s_transientErrors.Add(42108); // The SQL pool is warming up. Please try again. - transientErrors.Add(42109); + s_transientErrors.Add(42109); // Do federation errors deserve to be here ? // Note: Federation errors 10053 and 10054 might also deserve inclusion in your retry logic. //transientErrors.Add(10053); @@ -613,7 +613,7 @@ private bool IsTransientError(SqlException exc) } foreach (SqlError error in exc.Errors) { - if (transientErrors.Contains(error.Number)) + if (s_transientErrors.Contains(error.Number)) { // When server timeouts, connection is doomed. Reset here to allow reconnect. UnDoomThisConnection(); @@ -3118,14 +3118,14 @@ internal string UserServerName { get { - return m_userServerName; + return _userServerName; } private set { - m_userServerName = value; + _userServerName = value; } } - private string m_userServerName; + private string _userServerName; internal readonly string PreRoutingServerName; From 8f7a50a481cf7f35a7de22c33755df42756a8c1f Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 00:43:54 +0100 Subject: [PATCH 07/18] netcore, netfx: sync comments and whitespace --- .../SqlClient/SqlInternalConnectionTds.cs | 81 ++++++------ .../SqlClient/SqlInternalConnectionTds.cs | 125 ++++++++---------- 2 files changed, 100 insertions(+), 106 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 320a878fc9..b621f75ccc 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 @@ -432,8 +432,9 @@ internal SqlConnectionTimeoutErrorInternal TimeoutErrorInternal } // OTHER STATE VARIABLES AND REFERENCES - internal Guid _clientConnectionId = Guid.Empty; + + // Routing information (ROR) private Guid _originalClientConnectionId = Guid.Empty; private string _routingDestination = null; private readonly TimeoutTimer _timeout; @@ -456,7 +457,6 @@ internal SqlInternalConnectionTds( DbConnectionPool pool = null, Func> accessTokenCallback = null) : base(connectionOptions) - { #if DEBUG if (reconnectSessionData != null) @@ -524,7 +524,6 @@ internal SqlInternalConnectionTds( try { OpenLoginEnlist(_timeout, connectionOptions, credential, newPassword, newSecurePassword, redirectedUserInstance); - break; } catch (SqlException sqlex) @@ -697,6 +696,7 @@ protected override bool ReadyToPrepareTransaction { get { + // TODO: probably need to use a different method, but that's a different bug bool result = FindLiveReader(null) == null; // can't prepare with a live data reader... return result; } @@ -719,6 +719,16 @@ public int ServerProcessId } } + /// + /// Get boolean that specifies whether an enlisted transaction can be unbound from + /// the connection when that transaction completes. + /// + /// + /// This override always returns false. + /// + /// + /// The SqlInternalConnectionTds.CheckEnlistedTransactionBinding method handles implicit unbinding for disposed transactions. + /// protected override bool UnbindOnTransactionCompletion { get @@ -738,7 +748,7 @@ protected override bool UnbindOnTransactionCompletion [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // copied from Triaged.cs protected override void ChangeDatabaseInternal(string database) { - // Add brackets around database + // MDAC 73598 - add brackets around database database = SqlConnection.FixupDatabaseTransactionName(database); Task executeTask = _parser.TdsExecuteSQLBatch("use " + database, ConnectionOptions.ConnectTimeout, null, _parser._physicalStateObj, sync: true); Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes"); @@ -798,7 +808,7 @@ internal override void ValidateConnectionForExecute(SqlCommand command) { // if MARS is on, then a datareader associated with the command exists // or if MARS is off, then a datareader exists - throw ADP.OpenReaderExists(parser.MARSOn); + throw ADP.OpenReaderExists(parser.MARSOn); // MDAC 66411 } else if (!parser.MARSOn && parser._physicalStateObj.HasPendingData) { @@ -1023,8 +1033,7 @@ internal void ExecuteTransaction2005( string transactionName, System.Data.IsolationLevel iso, SqlInternalTransaction internalTransaction, - bool isDelegateControlRequest - ) + bool isDelegateControlRequest) { TdsEnums.TransactionManagerRequestType requestType = TdsEnums.TransactionManagerRequestType.Begin; TdsEnums.TransactionManagerIsolationLevel isoLevel = TdsEnums.TransactionManagerIsolationLevel.ReadCommitted; @@ -1145,7 +1154,7 @@ bool isDelegateControlRequest } } - // _parser may be nulled out during TdsExecuteTransactionManagerRequest. + // SQLBU #406778 - _parser may be nulled out during TdsExecuteTransactionManagerRequest. // Only use local variable after this call. _parser.TdsExecuteTransactionManagerRequest(null, requestType, transactionName, isoLevel, ConnectionOptions.ConnectTimeout, internalTransaction, stateObj, isDelegateControlRequest); @@ -1171,6 +1180,7 @@ bool isDelegateControlRequest internal override void DelegatedTransactionEnded() { + // TODO: I don't like the way that this works, but I don't want to rototill the entire pooler to avoid this call. base.DelegatedTransactionEnded(); } @@ -1207,6 +1217,7 @@ private void CompleteLogin(bool enlistOK) SqlClientEventSource.Log.TryTraceEvent(" {0}, Server never sent the requested federated authentication info", ObjectID); throw SQL.ParsingError(ParsingErrorState.FedAuthInfoNotReceived); } + if (!_sessionRecoveryAcknowledged) { _currentSessionData = null; @@ -1374,9 +1385,10 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, requestedFeatures |= TdsEnums.FeatureExtension.AzureSQLSupport; } - // The SQLDNSCaching feature is implicitly set + // The SQLDNSCaching and JSON features are implicitly set requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport; + _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt); } @@ -1515,7 +1527,9 @@ private void LoginNoFailover(ServerInfo serverInfo, Debug.Assert(object.ReferenceEquals(connectionOptions, this.ConnectionOptions), "ConnectionOptions argument and property must be the same"); // consider removing the argument int routingAttempts = 0; ServerInfo originalServerInfo = serverInfo; // serverInfo may end up pointing to new object due to routing, original object is used to set CurrentDatasource + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, host={1}", ObjectID, serverInfo.UserServerName); + int sleepInterval = 100; //milliseconds to sleep (back off) between attempts. ResolveExtendedServerName(serverInfo, !redirectedUserInstance, connectionOptions); @@ -1817,6 +1831,7 @@ TimeoutTimer timeout intervalTimer, withFailover: true ); + int routingAttempts = 0; while (RoutingInfo != null) { @@ -1828,7 +1843,7 @@ TimeoutTimer timeout SqlClientEventSource.Log.TryTraceEvent(" Routed to {0}", RoutingInfo.ServerName); - if(_parser != null) + if (_parser != null) { _parser.Disconnect(); } @@ -1842,7 +1857,7 @@ TimeoutTimer timeout _originalClientConnectionId = _clientConnectionId; _routingDestination = currentServerInfo.UserServerName; - // restore properties that could be changed by the environemnt tokens + // restore properties that could be changed by the environment tokens _currentPacketSize = connectionOptions.PacketSize; _currentLanguage = _originalLanguage = ConnectionOptions.CurrentLanguage; CurrentDatabase = _originalDatabase = connectionOptions.InitialCatalog; @@ -1856,6 +1871,7 @@ TimeoutTimer timeout intervalTimer, withFailover: true); } + break; // leave the while loop -- we've successfully connected } catch (SqlException sqlex) @@ -1966,12 +1982,11 @@ private void ResolveExtendedServerName(ServerInfo serverInfo, bool aliasLookup, } // Common code path for making one attempt to establish a connection and log in to server. - private void AttemptOneLogin( - ServerInfo serverInfo, - string newPassword, - SecureString newSecurePassword, - TimeoutTimer timeout, - bool withFailover = false) + private void AttemptOneLogin(ServerInfo serverInfo, + string newPassword, + SecureString newSecurePassword, + TimeoutTimer timeout, + bool withFailover = false) { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, timeout={1}[msec], server={2}", ObjectID, timeout.MillisecondsRemaining, serverInfo.ExtendedServerName); RoutingInfo = null; // forget routing information @@ -1998,8 +2013,6 @@ private void AttemptOneLogin( _timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.PostLogin); } - - //////////////////////////////////////////////////////////////////////////////////////// // PREPARED COMMAND METHODS //////////////////////////////////////////////////////////////////////////////////////// @@ -2235,7 +2248,6 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) // We want to refresh the token, if taking the lock on the authentication context is successful. bool attemptRefreshTokenLocked = false; - if (_dbConnectionPool != null) { Debug.Assert(_dbConnectionPool.AuthenticationContexts != null); @@ -2291,9 +2303,9 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) // If the lock could not be obtained, it will return false, without attempting to fetch a new token. attemptRefreshTokenLocked = TryGetFedAuthTokenLocked(fedAuthInfo, dbConnectionPoolAuthenticationContext, out _fedAuthToken); - // If TryGetFedAuthTokenLocked returns true, it means lock was obtained and fedAuthToken should not be null. + // If TryGetFedAuthTokenLocked returns true, it means lock was obtained and _fedAuthToken should not be null. // If there was an exception in retrieving the new token, TryGetFedAuthTokenLocked should have thrown, so we won't be here. - Debug.Assert(!attemptRefreshTokenLocked || _fedAuthToken != null, "Either Lock should not have been obtained or fedAuthToken should not be null."); + Debug.Assert(!attemptRefreshTokenLocked || _fedAuthToken != null, "Either Lock should not have been obtained or _fedAuthToken should not be null."); Debug.Assert(!attemptRefreshTokenLocked || _newDbConnectionPoolAuthenticationContext != null, "Either Lock should not have been obtained or _newDbConnectionPoolAuthenticationContext should not be null."); // Indicate in EventSource Trace that we are successful with the update. @@ -2311,7 +2323,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { // Get the Federated Authentication Token. _fedAuthToken = GetFedAuthToken(fedAuthInfo); - Debug.Assert(_fedAuthToken != null, "fedAuthToken should not be null."); + Debug.Assert(_fedAuthToken != null, "_fedAuthToken should not be null."); if (_dbConnectionPool != null) { @@ -2327,7 +2339,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) else if (!attemptRefreshTokenLocked) { Debug.Assert(dbConnectionPoolAuthenticationContext != null, "dbConnectionPoolAuthenticationContext should not be null."); - Debug.Assert(_fedAuthToken == null, "fedAuthToken should be null in this case."); + Debug.Assert(_fedAuthToken == null, "_fedAuthToken should be null in this case."); Debug.Assert(_newDbConnectionPoolAuthenticationContext == null, "_newDbConnectionPoolAuthenticationContext should be null."); _fedAuthToken = new SqlFedAuthToken(); @@ -2338,7 +2350,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) _fedAuthToken.expirationFileTime = dbConnectionPoolAuthenticationContext.ExpirationTime.ToFileTime(); } - Debug.Assert(_fedAuthToken != null && _fedAuthToken.accessToken != null, "fedAuthToken and fedAuthToken.accessToken cannot be null."); + Debug.Assert(_fedAuthToken != null && _fedAuthToken.accessToken != null, "_fedAuthToken and _fedAuthToken.accessToken cannot be null."); _parser.SendFedAuthToken(_fedAuthToken); } @@ -2597,14 +2609,8 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) throw ADP.CreateSqlException(msalException, ConnectionOptions, this, username); } - SqlClientEventSource.Log.TryAdvancedTraceEvent( - " {0}, sleeping {1}[Milliseconds]", - ObjectID, - sleepInterval); - SqlClientEventSource.Log.TryAdvancedTraceEvent( - " {0}, remaining {1}[Milliseconds]", - ObjectID, - _timeout.MillisecondsRemaining); + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, sleeping {1}[Milliseconds]", ObjectID, sleepInterval); + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, remaining {1}[Milliseconds]", ObjectID, _timeout.MillisecondsRemaining); Thread.Sleep(sleepInterval); sleepInterval *= 2; @@ -2721,6 +2727,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) case TdsEnums.FEATUREEXT_FEDAUTH: { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for federated authentication", ObjectID); + if (!_federatedAuthenticationRequested) { SqlClientEventSource.Log.TryTraceEvent(" {0}, Did not request federated authentication", ObjectID); @@ -2743,6 +2750,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) default: Debug.Fail("Unknown _fedAuthLibrary type"); + SqlClientEventSource.Log.TryTraceEvent(" {0}, Attempting to use unknown federated authentication library", ObjectID); throw SQL.ParsingErrorLibraryType(ParsingErrorState.FedAuthFeatureAckUnknownLibraryType, (int)_fedAuthFeatureExtensionData.libraryType); } @@ -2802,6 +2810,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) } break; } + case TdsEnums.FEATUREEXT_AZURESQLSUPPORT: { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for AzureSQLSupport", ObjectID); @@ -2817,10 +2826,10 @@ internal void OnFeatureExtAck(int featureId, byte[] data) if ((data[0] & 1) == 1 && SqlClientEventSource.Log.IsTraceEnabled()) { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, FailoverPartner enabled with Readonly intent for AzureSQL DB", ObjectID); - } break; } + case TdsEnums.FEATUREEXT_DATACLASSIFICATION: { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for DATACLASSIFICATION", ObjectID); @@ -2828,7 +2837,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) if (data.Length < 1) { SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown token for DATACLASSIFICATION", ObjectID); - throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); } byte supportedDataClassificationVersion = data[0]; @@ -2841,13 +2849,13 @@ internal void OnFeatureExtAck(int featureId, byte[] data) if (data.Length != 2) { SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown token for DATACLASSIFICATION", ObjectID); - throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); } byte enabled = data[1]; _parser.DataClassificationVersion = (enabled == 0) ? TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED : supportedDataClassificationVersion; break; } + case TdsEnums.FEATUREEXT_UTF8SUPPORT: { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for UTF8 support", ObjectID); @@ -2855,7 +2863,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) if (data.Length < 1) { SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown value for UTF8 support", ObjectID); - throw SQL.ParsingError(); } break; 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 8b69d2ae05..aa4c983fa3 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 @@ -155,7 +155,7 @@ private int accessTokenExpirationBufferTime } /// - /// Get or set if SQLDNSCaching FeatureExtAck is supported by the server. + /// Get or set if SQLDNSCaching is supported by the server. /// internal bool IsSQLDNSCachingSupported { @@ -189,7 +189,7 @@ internal bool IsSQLDNSRetryEnabled private bool _DNSCachingBeforeRedirect = false; /// - /// Get or set if the control ring send redirect token and SQLDNSCaching FeatureExtAck with true + /// Get or set if the control ring send redirect token and feature ext ack with true for DNSCaching /// internal bool IsDNSCachingBeforeRedirectSupported { @@ -290,7 +290,7 @@ internal SessionData CurrentSessionData // 2. _parserLock will also be taken during close (to prevent closing in the middle of a write) // 3. Whenever you have the _parserLock and are calling a method that would cause the connection to close if it failed (with the exception of any writing method), you MUST set ThreadHasParserLockForClose to true // * This is to prevent the connection deadlocking with itself (since you already have the _parserLock, and Closing the connection will attempt to re-take that lock) - // * It is safe to set ThreadHasParserLockForClose to true when writing as well, but it is unneccesary + // * It is safe to set ThreadHasParserLockForClose to true when writing as well, but it is unnecessary // * If you have a method that takes _parserLock, it is a good idea check ThreadHasParserLockForClose first (if you don't expect _parserLock to be taken by something higher on the stack, then you should at least assert that it is false) // 4. ThreadHasParserLockForClose is thread-specific - this means that you must set it to false before returning a Task, and set it back to true in the continuation // 5. ThreadHasParserLockForClose should only be modified if you currently own the _parserLock @@ -377,7 +377,7 @@ internal bool CanBeReleasedFromAnyThread } } - // Necessary but not sufficient condition for thread to have lock (since sempahore may be obtained by any thread) + // Necessary but not sufficient condition for thread to have lock (since semaphore may be obtained by any thread) internal bool ThreadMayHaveLock() { return Monitor.IsEntered(_semaphore) || _semaphore.CurrentCount == 0; @@ -396,7 +396,6 @@ internal SqlConnectionTimeoutErrorInternal TimeoutErrorInternal } // OTHER STATE VARIABLES AND REFERENCES - internal Guid _clientConnectionId = Guid.Empty; // Routing information (ROR) @@ -410,7 +409,7 @@ static SqlInternalConnectionTds() populateTransientErrors(); } - // although the new password is generally not used it must be passed to the c'tor + // although the new password is generally not used it must be passed to the ctor // the new Login7 packet will always write out the new password (or a length of zero and no bytes if not present) // internal SqlInternalConnectionTds( @@ -429,7 +428,6 @@ internal SqlInternalConnectionTds( Func> accessTokenCallback = null) : base(connectionOptions) { - #if DEBUG if (reconnectSessionData != null) { @@ -511,7 +509,7 @@ internal SqlInternalConnectionTds( { _timeout = TimeoutTimer.StartSecondsTimeout(connectionOptions.ConnectTimeout); - // If transient fault handling is enabled then we can retry the login upto the ConnectRetryCount. + // If transient fault handling is enabled then we can retry the login up to the ConnectRetryCount. int connectionEstablishCount = applyTransientFaultHandling ? connectionOptions.ConnectRetryCount + 1 : 1; int transientRetryIntervalInMilliSeconds = connectionOptions.ConnectRetryInterval * 1000; // Max value of transientRetryInterval is 60*1000 ms. The max value allowed for ConnectRetryInterval is 60 for (int i = 0; i < connectionEstablishCount; i++) @@ -828,7 +826,7 @@ public override void Dispose() } } finally - { // UNDONE: MDAC 77928 + { // close will always close, even if exception is thrown // remember to null out any object references _loginAck = null; @@ -898,7 +896,7 @@ internal override void ValidateConnectionForExecute(SqlCommand command) /// /// When using Implicit transaction unbinding, /// verify that the enlisted transaction is active. - /// If it is not active, and the transaction object has been diposed, unbind from the transaction. + /// If it is not active, and the transaction object has been disposed, unbind from the transaction. /// If it is not active and not disposed, throw an exception. /// /// @@ -991,7 +989,6 @@ protected override void InternalDeactivate() Debug.Assert(_parser != null || IsConnectionDoomed, "Deactivating a disposed connection?"); if (_parser != null) { - _parser.Deactivate(IsConnectionDoomed); if (!IsConnectionDoomed) @@ -1007,7 +1004,7 @@ private void ResetConnection() { // For implicit pooled connections, if connection reset behavior is specified, // reset the database and language properties back to default. It is important - // to do this on activate so that the hashtable is correct before SqlConnection + // to do this on activate so that the dictionary is correct before SqlConnection // obtains a clone. Debug.Assert(!HasLocalTransactionFromAPI, "Upon ResetConnection SqlInternalConnectionTds has a currently ongoing local transaction."); @@ -1021,7 +1018,7 @@ private void ResetConnection() // https://github.com/dotnet/SqlClient/issues/2970 _parser.PrepareResetConnection(EnlistedTransaction is not null && Pool is not null); - // Reset hashtable values, since calling reset will not send us env_changes. + // Reset dictionary values, since calling reset will not send us env_changes. CurrentDatabase = _originalDatabase; _currentLanguage = _originalLanguage; } @@ -1179,19 +1176,17 @@ internal void ExecuteTransaction2005( } } - - // SQLBUDT #20010853 - Promote, Commit and Rollback requests for // delegated transactions often happen while there is an open result // set, so we need to handle them by using a different MARS session, // otherwise we'll write on the physical state objects while someone // else is using it. When we don't have MARS enabled, we need to - // lock the physical state object to syncronize it's use at least + // lock the physical state object to synchronize it's use at least // until we increment the open results count. Once it's been // incremented the delegated transaction requests will fail, so they // won't stomp on anything. // - // We need to keep this lock through the duration of the TM reqeuest + // We need to keep this lock through the duration of the TM request // so that we won't hijack a different request's data stream and a // different request won't hijack ours, so we have a lock here on // an object that the ExecTMReq will also lock, but since we're on @@ -1214,7 +1209,7 @@ internal void ExecuteTransaction2005( } } - // SQLBU #406778 - _parser may be nulled out during TdsExecuteTrannsactionManagerRequest. + // SQLBU #406778 - _parser may be nulled out during TdsExecuteTransactionManagerRequest. // Only use local variable after this call. _parser.TdsExecuteTransactionManagerRequest(null, requestType, transactionName, isoLevel, ConnectionOptions.ConnectTimeout, internalTransaction, stateObj, isDelegateControlRequest); @@ -1322,6 +1317,7 @@ private void CompleteLogin(bool enlistOK) Transaction tx = ADP.GetCurrentTransaction(); Enlist(tx); } + _parser._physicalStateObj.SniContext = SniContext.Snix_Login; } @@ -1366,9 +1362,9 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, login.language = _currentLanguage; if (!login.userInstance) - { // Do not send attachdbfilename or database to SSE primary instance + { + // Do not send attachdbfilename or database to SSE primary instance login.database = CurrentDatabase; - ; login.attachDBFilename = ConnectionOptions.AttachDBFilename; } @@ -1435,7 +1431,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, _federatedAuthenticationRequested = true; } - // The TCE, DATACLASSIFICATION and GLOBALTRANSACTIONS, UTF8 support feature are implicitly requested + // The GLOBALTRANSACTIONS, DATACLASSIFICATION, TCE, and UTF8 support features are implicitly requested requestedFeatures |= TdsEnums.FeatureExtension.Tce | TdsEnums.FeatureExtension.DataClassification | TdsEnums.FeatureExtension.GlobalTransactions; requestedFeatures |= TdsEnums.FeatureExtension.UTF8Support; @@ -1446,9 +1442,8 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, requestedFeatures |= TdsEnums.FeatureExtension.AzureSQLSupport; } - // The SQLDNSCaching feature is implicitly set + // The SQLDNSCaching and JSON features are implicitly set requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; - requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport; _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt); @@ -1464,14 +1459,16 @@ private void LoginFailure() // no-op, so no issues there. if (_parser != null) { - _parser.Disconnect(); } - // TODO: Need a performance counter for Failed Connections } - private void OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, - string newPassword, SecureString newSecurePassword, bool redirectedUserInstance) + private void OpenLoginEnlist(TimeoutTimer timeout, + SqlConnectionString connectionOptions, + SqlCredential credential, + string newPassword, + SecureString newSecurePassword, + bool redirectedUserInstance) { bool useFailoverPartner; // should we use primary or secondary first ServerInfo dataSource = new ServerInfo(connectionOptions); @@ -1514,7 +1511,8 @@ private void OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectio else { _timeoutErrorInternal.SetFailoverScenario(false); // not a failover scenario - LoginNoFailover(dataSource, + LoginNoFailover( + dataSource, newPassword, newSecurePassword, redirectedUserInstance, @@ -1543,7 +1541,6 @@ private void OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectio } catch (Exception e) { - // UNDONE - should not be catching all exceptions!!! if (ADP.IsCatchableExceptionType(e)) { LoginFailure(); @@ -1561,11 +1558,10 @@ private void OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectio // to connect. private bool IsDoNotRetryConnectError(SqlException exc) { - return (TdsEnums.LOGON_FAILED == exc.Number) // actual logon failed, i.e. bad password || (TdsEnums.PASSWORD_EXPIRED == exc.Number) // actual logon failed, i.e. password isExpired - || (TdsEnums.IMPERSONATION_FAILED == exc.Number) // Insuficient privelege for named pipe, among others - || exc._doNotReconnect; // Exception explicitly supressed reconnection attempts + || (TdsEnums.IMPERSONATION_FAILED == exc.Number) // Insufficient privilege for named pipe, among others + || exc._doNotReconnect; // Exception explicitly suppressed reconnection attempts } // Attempt to login to a host that does not have a failover partner @@ -1577,10 +1573,14 @@ private bool IsDoNotRetryConnectError(SqlException exc) // DEVNOTE: The logic in this method is paralleled by the logic in LoginWithFailover. // Changes to either one should be examined to see if they need to be reflected in the other // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureString newSecurePassword, bool redirectedUserInstance, - SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout) + private void LoginNoFailover(ServerInfo serverInfo, + string newPassword, + SecureString newSecurePassword, + bool redirectedUserInstance, + SqlConnectionString connectionOptions, + SqlCredential credential, + TimeoutTimer timeout) { - Debug.Assert(object.ReferenceEquals(connectionOptions, this.ConnectionOptions), "ConnectionOptions argument and property must be the same"); // consider removing the argument int routingAttempts = 0; ServerInfo originalServerInfo = serverInfo; // serverInfo may end up pointing to new object due to routing, original object is used to set CurrentDatasource @@ -1626,7 +1626,6 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt TimeoutTimer attemptOneLoginTimeout = timeout; while (true) { - Boolean isFirstTransparentAttempt = connectionOptions.TransparentNetworkIPResolution && !disableTnir && attemptNumber == 1; if (isParallel) @@ -1666,7 +1665,7 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt try { - // UNDONE: ITEM12001110 (DB Mirroring Reconnect) Old behavior of not truly honoring timeout presevered + // UNDONE: ITEM12001110 (DB Mirroring Reconnect) Old behavior of not truly honoring timeout preserved // for non-failover, non-MSF scenarios to avoid breaking changes as part of a QFE. Consider fixing timeout // handling in next full release and removing ignoreSniOpenTimeout parameter. @@ -1684,14 +1683,13 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt if (connectionOptions.MultiSubnetFailover && ServerProvidedFailOverPartner != null) { - // connection succeeded: trigger exception if server sends failover partner and MultiSubnetFailover is used. + // connection succeeded: trigger exception if server sends failover partner and MultiSubnetFailover is used throw SQL.MultiSubnetFailoverWithFailoverPartner(serverProvidedFailoverPartner: true, internalConnection: this); } if (_routingInfo != null) { SqlClientEventSource.Log.TryTraceEvent(" Routed to {0}", serverInfo.ExtendedServerName); - if (routingAttempts > _maxNumberOfRedirectRoute) { throw SQL.ROR_RecursiveRoutingNotSupported(this, _maxNumberOfRedirectRoute); @@ -1734,8 +1732,9 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt || TdsParserState.Closed != _parser.State || IsDoNotRetryConnectError(sqlex) || timeout.IsExpired) - { // no more time to try again - throw; // Caller will call LoginFailure() + { + // no more time to try again + throw; // Caller will call LoginFailure() } // Check sleep interval to make sure we won't exceed the timeout @@ -1744,8 +1743,6 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt { throw; } - - // TODO: Stash parser away somewhere so we can examine it's state during debugging } // We only get here when we failed to connect, but are going to re-try @@ -1780,7 +1777,6 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt // Sleep for a bit to prevent clogging the network with requests, // then update sleep interval for next iteration (max 1 second interval) SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, sleeping {1}[milisec]", ObjectID, sleepInterval); - Thread.Sleep(sleepInterval); sleepInterval = (sleepInterval < 500) ? sleepInterval * 2 : 1000; } @@ -1865,7 +1861,6 @@ private void LoginWithFailover( TimeoutTimer timeout ) { - Debug.Assert(!connectionOptions.MultiSubnetFailover, "MultiSubnetFailover should not be set if failover partner is used"); SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, useFailover={1}[bool], primary={2}, failover={3}", ObjectID, useFailoverHost, primaryServerInfo.UserServerName, failoverHost); @@ -1877,8 +1872,7 @@ TimeoutTimer timeout ResolveExtendedServerName(primaryServerInfo, !redirectedUserInstance, connectionOptions); 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); } @@ -1996,8 +1990,7 @@ TimeoutTimer timeout newPassword, newSecurePassword, intervalTimer, - withFailover: true - ); + withFailover: true); } break; // leave the while loop -- we've successfully connected @@ -2029,8 +2022,6 @@ TimeoutTimer timeout throw; } } - - // TODO: Stash parser away somewhere so we can examine it's state during debugging } // We only get here when we failed to connect, but are going to re-try @@ -2112,10 +2103,15 @@ private void ResolveExtendedServerName(ServerInfo serverInfo, bool aliasLookup, } // Common code path for making one attempt to establish a connection and log in to server. - private void AttemptOneLogin(ServerInfo serverInfo, string newPassword, SecureString newSecurePassword, TimeoutTimer timeout, bool withFailover = false, bool isFirstTransparentAttempt = true, bool disableTnir = false) + private void AttemptOneLogin(ServerInfo serverInfo, + string newPassword, + SecureString newSecurePassword, + TimeoutTimer timeout, + bool withFailover = false, + bool isFirstTransparentAttempt = true, + bool disableTnir = false) { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, timeout={1}[msec], server={2}", ObjectID, timeout.MillisecondsRemaining, serverInfo.ExtendedServerName); - _routingInfo = null; // forget routing information _parser._physicalStateObj.SniContext = SniContext.Snix_Connect; @@ -2142,7 +2138,6 @@ private void AttemptOneLogin(ServerInfo serverInfo, string newPassword, SecureSt _timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.PostLogin); } - internal void FailoverPermissionDemand() { if (PoolGroupProviderInfo != null) @@ -2176,11 +2171,10 @@ protected override void ReleaseAdditionalLocksForClose(bool lockToken) } } - // called by SqlConnection.RepairConnection which is a relatevly expensive way of repair inner connection + // called by SqlConnection.RepairConnection which is a relatively expensive way of repair inner connection // prior to execution of request, used from EnlistTransaction, EnlistDistributedTransaction and ChangeDatabase internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = 0) { - Debug.Assert(!ThreadHasParserLockForClose, "Cannot call this method if caller has parser lock"); if (ThreadHasParserLockForClose) { @@ -2244,7 +2238,6 @@ internal void BreakConnection() SqlConnection connection = Connection; SqlClientEventSource.Log.TryTraceEvent(" {0}, Breaking connection.", ObjectID); DoomThisConnection(); // Mark connection as unusable, so it will be destroyed - if (connection != null) { connection.Close(); @@ -2265,7 +2258,7 @@ internal void OnEnvChange(SqlEnvChange rec) switch (rec._type) { case TdsEnums.ENV_DATABASE: - // If connection is not open and recovery is not in progresss, store the server value as the original. + // If connection is not open and recovery is not in progress, store the server value as the original. if (!_fConnectionOpen && _recoverySessionData == null) { _originalDatabase = rec._newValue; @@ -2275,13 +2268,13 @@ internal void OnEnvChange(SqlEnvChange rec) break; case TdsEnums.ENV_LANG: - // If connection is not open and recovery is not in progresss, store the server value as the original. + // If connection is not open and recovery is not in progress, store the server value as the original. if (!_fConnectionOpen && _recoverySessionData == null) { _originalLanguage = rec._newValue; } - _currentLanguage = rec._newValue; // TODO: finish this. + _currentLanguage = rec._newValue; break; case TdsEnums.ENV_PACKETSIZE: @@ -2362,7 +2355,6 @@ internal void OnEnvChange(SqlEnvChange rec) internal void OnLoginAck(SqlLoginAck rec) { _loginAck = rec; - // UNDONE: throw an error if this is not 7.0 or 7.1[5]. if (_recoverySessionData != null) { if (_recoverySessionData._tdsVersion != rec.tdsVersion) @@ -2464,7 +2456,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) Debug.Assert(!attemptRefreshTokenLocked || _fedAuthToken != null, "Either Lock should not have been obtained or _fedAuthToken should not be null."); Debug.Assert(!attemptRefreshTokenLocked || _newDbConnectionPoolAuthenticationContext != null, "Either Lock should not have been obtained or _newDbConnectionPoolAuthenticationContext should not be null."); - // Indicate in Bid Trace that we are successful with the update. + // Indicate in EventSource Trace that we are successful with the update. if (attemptRefreshTokenLocked) { SqlClientEventSource.Log.TryTraceEvent(" {0}, The attempt to get a new access token succeeded under the locked mode.", ObjectID); @@ -2575,7 +2567,6 @@ internal bool TryGetFedAuthTokenLocked(SqlFedAuthInfo fedAuthInfo, DbConnectionP /// SqlFedAuthToken internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { - Debug.Assert(fedAuthInfo != null, "fedAuthInfo should not be null."); // No:of milliseconds to sleep for the inital back off. @@ -2746,9 +2737,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) // Deal with normal MsalExceptions. catch (MsalException msalException) { - if (MsalError.UnknownError != msalException.ErrorCode - || _timeout.IsExpired - || _timeout.MillisecondsRemaining <= sleepInterval) + if (MsalError.UnknownError != msalException.ErrorCode || _timeout.IsExpired || _timeout.MillisecondsRemaining <= sleepInterval) { SqlClientEventSource.Log.TryTraceEvent(" {0}", msalException.ErrorCode); @@ -2973,7 +2962,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) if (data.Length < 1) { SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown token for DATACLASSIFICATION", ObjectID); - throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); } byte supportedDataClassificationVersion = data[0]; @@ -3032,10 +3020,9 @@ internal void OnFeatureExtAck(int featureId, byte[] data) _cleanSQLDNSCaching = true; } - // need to add more steps for phrase 2 + // need to add more steps for phase 2 // get IPv4 + IPv6 + Port number // not put them in the DNS cache at this point but need to store them somewhere - // generate pendingSQLDNSObject and turn on IsSQLDNSRetryEnabled flag break; From 9ac89d1857e395f010a563adae772a9aa56a91ba Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 00:50:11 +0100 Subject: [PATCH 08/18] netfx: convert RoutingInfo to automatic property --- .../SqlClient/SqlInternalConnectionTds.cs | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) 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 aa4c983fa3..43039cfccc 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 @@ -399,7 +399,6 @@ internal SqlConnectionTimeoutErrorInternal TimeoutErrorInternal internal Guid _clientConnectionId = Guid.Empty; // Routing information (ROR) - RoutingInfo _routingInfo = null; private Guid _originalClientConnectionId = Guid.Empty; private string _routingDestination = null; private readonly TimeoutTimer _timeout; @@ -645,13 +644,7 @@ internal string RoutingDestination } } - internal RoutingInfo RoutingInfo - { - get - { - return _routingInfo; - } - } + internal RoutingInfo RoutingInfo { get; private set; } = null; internal override SqlInternalTransaction CurrentTransaction { @@ -1259,7 +1252,7 @@ private void CompleteLogin(bool enlistOK) { _parser.Run(RunBehavior.UntilDone, null, null, null, _parser._physicalStateObj); - if (_routingInfo == null) + if (RoutingInfo == null) { // ROR should not affect state of connection recovery if (_federatedAuthenticationRequested && !_federatedAuthenticationAcknowledged) @@ -1311,7 +1304,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 && _routingInfo == null) + if (enlistOK && ConnectionOptions.Enlist && RoutingInfo == null) { _parser._physicalStateObj.SniContext = SniContext.Snix_AutoEnlist; Transaction tx = ADP.GetCurrentTransaction(); @@ -1687,7 +1680,7 @@ private void LoginNoFailover(ServerInfo serverInfo, throw SQL.MultiSubnetFailoverWithFailoverPartner(serverProvidedFailoverPartner: true, internalConnection: this); } - if (_routingInfo != null) + if (RoutingInfo != null) { SqlClientEventSource.Log.TryTraceEvent(" Routed to {0}", serverInfo.ExtendedServerName); if (routingAttempts > _maxNumberOfRedirectRoute) @@ -1700,7 +1693,7 @@ private void LoginNoFailover(ServerInfo serverInfo, throw SQL.ROR_TimeoutAfterRoutingInfo(this); } - serverInfo = new ServerInfo(ConnectionOptions, _routingInfo, serverInfo.ResolvedServerName, serverInfo.ServerSPN); + serverInfo = new ServerInfo(ConnectionOptions, RoutingInfo, serverInfo.ResolvedServerName, serverInfo.ServerSPN); _timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.RoutingDestination); _originalClientConnectionId = _clientConnectionId; _routingDestination = serverInfo.UserServerName; @@ -1955,7 +1948,7 @@ TimeoutTimer timeout ); int routingAttempts = 0; - while (_routingInfo != null) + while (RoutingInfo != null) { if (routingAttempts > _maxNumberOfRedirectRoute) { @@ -1963,7 +1956,7 @@ TimeoutTimer timeout } routingAttempts++; - SqlClientEventSource.Log.TryTraceEvent(" Routed to {0}", _routingInfo.ServerName); + SqlClientEventSource.Log.TryTraceEvent(" Routed to {0}", RoutingInfo.ServerName); if (_parser != null) { @@ -1973,7 +1966,7 @@ TimeoutTimer timeout _parser = new TdsParser(ConnectionOptions.MARS, ConnectionOptions.Asynchronous); Debug.Assert(SniContext.Undefined == Parser._physicalStateObj.SniContext, $"SniContext should be Undefined; actual Value: {Parser._physicalStateObj.SniContext}"); - currentServerInfo = new ServerInfo(ConnectionOptions, _routingInfo, currentServerInfo.ResolvedServerName, currentServerInfo.ServerSPN); + currentServerInfo = new ServerInfo(ConnectionOptions, RoutingInfo, currentServerInfo.ResolvedServerName, currentServerInfo.ServerSPN); _timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.RoutingDestination); _originalClientConnectionId = _clientConnectionId; _routingDestination = currentServerInfo.UserServerName; @@ -2112,7 +2105,7 @@ private void AttemptOneLogin(ServerInfo serverInfo, bool disableTnir = false) { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, timeout={1}[msec], server={2}", ObjectID, timeout.MillisecondsRemaining, serverInfo.ExtendedServerName); - _routingInfo = null; // forget routing information + RoutingInfo = null; // forget routing information _parser._physicalStateObj.SniContext = SniContext.Snix_Connect; @@ -2248,7 +2241,7 @@ internal bool IgnoreEnvChange { // true if we are only draining environment change tokens, used by TdsParser get { - return _routingInfo != null; // connection was routed, ignore rest of env change + return RoutingInfo != null; // connection was routed, ignore rest of env change } } @@ -2343,7 +2336,7 @@ internal void OnEnvChange(SqlEnvChange rec) { throw SQL.ROR_InvalidRoutingInfo(this); } - _routingInfo = rec._newRoutingInfo; + RoutingInfo = rec._newRoutingInfo; break; default: @@ -2772,12 +2765,9 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) internal void OnFeatureExtAck(int featureId, byte[] data) { - if (_routingInfo != null) + if (RoutingInfo != null && TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) { - if (TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) - { - return; - } + return; } switch (featureId) @@ -3008,7 +2998,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) IsSQLDNSCachingSupported = true; _cleanSQLDNSCaching = false; - if (_routingInfo != null) + if (RoutingInfo != null) { IsDNSCachingBeforeRedirectSupported = true; } From f8940d9cd82dcfad6b8dcc83acf6b908404206bb Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 00:55:29 +0100 Subject: [PATCH 09/18] netfx: reformatted HashSet to align with netcore --- .../SqlClient/SqlInternalConnectionTds.cs | 97 +++++++++---------- 1 file changed, 48 insertions(+), 49 deletions(-) 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 43039cfccc..22ea7f2aad 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 @@ -251,7 +251,54 @@ internal bool IsDNSCachingBeforeRedirectSupported // If the context is expiring within the next 10 mins, then create a new context, irrespective of if another thread is trying to do the same. private static readonly TimeSpan _dbAuthenticationContextUnLockedRefreshTimeSpan = new TimeSpan(hours: 0, minutes: 10, seconds: 00); - private static HashSet s_transientErrors = new HashSet(); + // The errors in the transient error set are contained in + // https://azure.microsoft.com/en-us/documentation/articles/sql-database-develop-error-messages/#transient-faults-connection-loss-and-other-temporary-errors + private static readonly HashSet s_transientErrors = new HashSet + { + // SQL Error Code: 4060 + // Cannot open database "%.*ls" requested by the login. The login failed. + 4060, + + // SQL Error Code: 10928 + // Resource ID: %d. The %s limit for the database is %d and has been reached. + 10928, + + // SQL Error Code: 10929 + // Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. + // However, the server is currently too busy to support requests greater than %d for this database. + 10929, + + // SQL Error Code: 40197 + // You will receive this error, when the service is down due to software or hardware upgrades, hardware failures, + // or any other failover problems. The error code (%d) embedded within the message of error 40197 provides + // additional information about the kind of failure or failover that occurred. Some examples of the error codes are + // embedded within the message of error 40197 are 40020, 40143, 40166, and 40540. + 40197, + 40020, + 40143, + 40166, + + // The service has encountered an error processing your request. Please try again. + 40540, + + // The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. + 40501, + + // Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. + // If the problem persists, contact customer support, and provide them the session tracing ID of '%.*ls'. + 40613, + + // Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. + 42108, + + // The SQL pool is warming up. Please try again. + 42109 + + // Do federation errors deserve to be here ? + // Note: Federation errors 10053 and 10054 might also deserve inclusion in your retry logic. + // 10053 + // 10054 + }; internal SessionData CurrentSessionData { @@ -403,11 +450,6 @@ internal SqlConnectionTimeoutErrorInternal TimeoutErrorInternal private string _routingDestination = null; private readonly TimeoutTimer _timeout; - static SqlInternalConnectionTds() - { - populateTransientErrors(); - } - // although the new password is generally not used it must be passed to the ctor // the new Login7 packet will always write out the new password (or a length of zero and no bytes if not present) // @@ -558,49 +600,6 @@ internal SqlInternalConnectionTds( SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, constructed new TDS internal connection", ObjectID); } - // The errors in the transient error set are contained in - // https://azure.microsoft.com/en-us/documentation/articles/sql-database-develop-error-messages/#transient-faults-connection-loss-and-other-temporary-errors - private static void populateTransientErrors() - { - // SQL Error Code: 4060 - // Cannot open database "%.*ls" requested by the login. The login failed. - s_transientErrors.Add(4060); - // SQL Error Code: 10928 - // Resource ID: %d. The %s limit for the database is %d and has been reached. - s_transientErrors.Add(10928); - // SQL Error Code: 10929 - // Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. - // However, the server is currently too busy to support requests greater than %d for this database. - s_transientErrors.Add(10929); - // SQL Error Code: 40197 - // You will receive this error, when the service is down due to software or hardware upgrades, hardware failures, - // or any other failover problems. The error code (%d) embedded within the message of error 40197 provides - // additional information about the kind of failure or failover that occurred. Some examples of the error codes are - // embedded within the message of error 40197 are 40020, 40143, 40166, and 40540. - s_transientErrors.Add(40197); - s_transientErrors.Add(40020); - s_transientErrors.Add(40143); - s_transientErrors.Add(40166); - // The service has encountered an error processing your request. Please try again. - s_transientErrors.Add(40540); - // The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. - s_transientErrors.Add(40501); - // Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. - // If the problem persists, contact customer support, and provide them the session tracing ID of '%.*ls'. - s_transientErrors.Add(40613); - - // Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. - s_transientErrors.Add(42108); - - // The SQL pool is warming up. Please try again. - s_transientErrors.Add(42109); - // Do federation errors deserve to be here ? - // Note: Federation errors 10053 and 10054 might also deserve inclusion in your retry logic. - //transientErrors.Add(10053); - //transientErrors.Add(10054); - } - - // Returns true if the Sql error is a transient. private bool IsTransientError(SqlException exc) { From f09ea7f9a2cc187964874b45d85a9b2606e2bb5d Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 01:03:03 +0100 Subject: [PATCH 10/18] netfx, netcore: formatting changes --- .../SqlClient/SqlInternalConnectionTds.cs | 8 ++--- .../SqlClient/SqlInternalConnectionTds.cs | 31 +++++++++++++------ 2 files changed, 24 insertions(+), 15 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 b621f75ccc..072c53d63f 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 @@ -879,12 +879,8 @@ internal void CheckEnlistedTransactionBinding() } } - internal override bool IsConnectionAlive(bool throwOnException) - { - bool isAlive = false; - isAlive = _parser._physicalStateObj.IsConnectionAlive(throwOnException); - return isAlive; - } + internal override bool IsConnectionAlive(bool throwOnException) => + _parser._physicalStateObj.IsConnectionAlive(throwOnException); //////////////////////////////////////////////////////////////////////////////////////// // POOLING METHODS 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 22ea7f2aad..77c21a283e 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 @@ -799,7 +799,7 @@ protected override void ChangeDatabaseInternal(string database) { // MDAC 73598 - add brackets around database database = SqlConnection.FixupDatabaseTransactionName(database); - System.Threading.Tasks.Task executeTask = _parser.TdsExecuteSQLBatch("use " + database, ConnectionOptions.ConnectTimeout, null, _parser._physicalStateObj, sync: true); + Task executeTask = _parser.TdsExecuteSQLBatch("use " + database, ConnectionOptions.ConnectTimeout, null, _parser._physicalStateObj, sync: true); Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes"); _parser.Run(RunBehavior.UntilDone, null, null, null, _parser._physicalStateObj); } @@ -1389,8 +1389,8 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, // in Login7, indicating the intent to use Active Directory Authentication for SQL Server. if (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive - || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault @@ -1424,9 +1424,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, } // The GLOBALTRANSACTIONS, DATACLASSIFICATION, TCE, and UTF8 support features are implicitly requested - requestedFeatures |= TdsEnums.FeatureExtension.Tce | TdsEnums.FeatureExtension.DataClassification | TdsEnums.FeatureExtension.GlobalTransactions; - - requestedFeatures |= TdsEnums.FeatureExtension.UTF8Support; + requestedFeatures |= TdsEnums.FeatureExtension.GlobalTransactions | TdsEnums.FeatureExtension.DataClassification | TdsEnums.FeatureExtension.Tce | TdsEnums.FeatureExtension.UTF8Support; // The AzureSQLSupport feature is implicitly set for ReadOnly login if (ConnectionOptions.ApplicationIntent == ApplicationIntent.ReadOnly) @@ -1653,7 +1651,7 @@ private void LoginNoFailover(ServerInfo serverInfo, _parser.Disconnect(); _parser = new TdsParser(ConnectionOptions.MARS, ConnectionOptions.Asynchronous); - Debug.Assert(SniContext.Undefined == Parser._physicalStateObj.SniContext, string.Format((IFormatProvider)null, "SniContext should be Undefined; actual Value: {0}", Parser._physicalStateObj.SniContext)); + Debug.Assert(SniContext.Undefined == Parser._physicalStateObj.SniContext, $"SniContext should be Undefined; actual Value: {Parser._physicalStateObj.SniContext}"); try { @@ -1909,7 +1907,7 @@ TimeoutTimer timeout } _parser = new TdsParser(ConnectionOptions.MARS, ConnectionOptions.Asynchronous); - Debug.Assert(SniContext.Undefined == Parser._physicalStateObj.SniContext, string.Format((IFormatProvider)null, "SniContext should be Undefined; actual Value: {0}", Parser._physicalStateObj.SniContext)); + Debug.Assert(SniContext.Undefined == Parser._physicalStateObj.SniContext, $"SniContext should be Undefined; actual Value: {Parser._physicalStateObj.SniContext}"); ServerInfo currentServerInfo; if (useFailoverHost) @@ -2370,11 +2368,11 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || _credential != null || _accessTokenCallback != null || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity - || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); Debug.Assert(fedAuthInfo != null, "info should not be null."); @@ -2745,7 +2743,21 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) // All other exceptions from MSAL/Azure Identity APIs catch (Exception e) { - throw SqlException.CreateException(new() { new(0, (byte)0x00, (byte)TdsEnums.FATAL_ERROR_CLASS, ConnectionOptions.DataSource, e.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0) }, "", this, e); + throw SqlException.CreateException( + new() + { + new( + 0, + (byte)0x00, + (byte)TdsEnums.FATAL_ERROR_CLASS, + ConnectionOptions.DataSource, + e.Message, + ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, + 0) + }, + "", + this, + e); } } @@ -2901,6 +2913,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) if (data.Length > 1) { + // Extract the type of enclave being used by the server. _parser.EnclaveType = Encoding.Unicode.GetString(data, 2, (data.Length - 2)); } From 85575104baeee59d66003d02720337867282547b Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 01:07:04 +0100 Subject: [PATCH 11/18] netcore: add CERs --- .../SqlClient/SqlInternalConnectionTds.cs | 57 ++++++++++++++++--- .../SqlClient/SqlInternalConnectionTds.cs | 1 + 2 files changed, 49 insertions(+), 9 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 072c53d63f..17e00e92b9 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 @@ -512,6 +512,9 @@ internal SqlInternalConnectionTds( _parserLock.Wait(canReleaseFromAnyThread: false); ThreadHasParserLockForClose = true; // In case of error, let ourselves know that we already own the parser lock +#if NETFRAMEWORK + RuntimeHelpers.PrepareConstrainedRegions(); +#endif try { _timeout = TimeoutTimer.StartSecondsTimeout(connectionOptions.ConnectTimeout); @@ -543,6 +546,21 @@ internal SqlInternalConnectionTds( } } } + catch (System.OutOfMemoryException) + { + DoomThisConnection(); + throw; + } + catch (System.StackOverflowException) + { + DoomThisConnection(); + throw; + } + catch (System.Threading.ThreadAbortException) + { + DoomThisConnection(); + throw; + } finally { ThreadHasParserLockForClose = false; @@ -2050,18 +2068,39 @@ internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = try { - Task reconnectTask = parent.ValidateAndReconnect(() => +#if NETFRAMEWORK + RuntimeHelpers.PrepareConstrainedRegions(); +#endif + try { - ThreadHasParserLockForClose = false; - _parserLock.Release(); - releaseConnectionLock = false; - }, timeout); - if (reconnectTask != null) + Task reconnectTask = parent.ValidateAndReconnect(() => + { + ThreadHasParserLockForClose = false; + _parserLock.Release(); + releaseConnectionLock = false; + }, timeout); + if (reconnectTask != null) + { + AsyncHelper.WaitForCompletion(reconnectTask, timeout); + return true; + } + return false; + } + catch (System.OutOfMemoryException) { - AsyncHelper.WaitForCompletion(reconnectTask, timeout); - return true; + DoomThisConnection(); + throw; + } + catch (System.StackOverflowException) + { + DoomThisConnection(); + throw; + } + catch (System.Threading.ThreadAbortException) + { + DoomThisConnection(); + throw; } - return false; } finally { 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 77c21a283e..760fdbb569 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 @@ -545,6 +545,7 @@ internal SqlInternalConnectionTds( _parserLock.Wait(canReleaseFromAnyThread: false); ThreadHasParserLockForClose = true; // In case of error, let ourselves know that we already own the parser lock + RuntimeHelpers.PrepareConstrainedRegions(); try { From 6e91f31146ed16975b80e065386e35dbff276363 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 01:09:10 +0100 Subject: [PATCH 12/18] netcore: Apply mask to TdsParser.EncryptionOptions --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 2 +- .../src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs | 2 +- 2 files changed, 2 insertions(+), 2 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 17e00e92b9..dffcbd84c4 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 @@ -1246,7 +1246,7 @@ private void CompleteLogin(bool enlistOK) _currentSessionData._initialCollation = _currentSessionData._collation; _currentSessionData._initialLanguage = _currentLanguage; } - bool isEncrypted = _parser.EncryptionOptions == EncryptionOptions.ON; + bool isEncrypted = (_parser.EncryptionOptions & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.ON; if (_recoverySessionData != null) { if (_recoverySessionData._encrypted != isEncrypted) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index 83b98a2b66..873d87c92e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -30,8 +30,8 @@ internal enum EncryptionOptions NOT_SUP, REQ, LOGIN, -#if NETFRAMEWORK OPTIONS_MASK = 0x3f, +#if NETFRAMEWORK CTAIP = 0x40, CLIENT_CERT = 0x80, #endif From bf5328d270093fe9bfc2d029cf20650e117ffe2c Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 01:13:31 +0100 Subject: [PATCH 13/18] netfx: synced use of connectionOptions variable rather than local property. netcore: synced explicit variable type in declaration. --- .../Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 4 ++-- .../Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 7 ++++--- 2 files changed, 6 insertions(+), 5 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 dffcbd84c4..417417cf67 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 @@ -2118,7 +2118,7 @@ internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = internal void BreakConnection() { - var connection = Connection; + SqlConnection connection = Connection; SqlClientEventSource.Log.TryTraceEvent(" {0}, Breaking connection.", ObjectID); DoomThisConnection(); // Mark connection as unusable, so it will be destroyed if (connection != null) @@ -2470,7 +2470,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) // Username to use in error messages. string username = null; - var authProvider = SqlAuthenticationProvider.GetProvider(ConnectionOptions.Authentication); + SqlAuthenticationProvider authProvider = SqlAuthenticationProvider.GetProvider(ConnectionOptions.Authentication); if (authProvider == null && _accessTokenCallback == null) throw SQL.CannotFindAuthProvider(ConnectionOptions.Authentication.ToString()); 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 760fdbb569..5106fd6f68 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 @@ -1961,7 +1961,8 @@ TimeoutTimer timeout _parser.Disconnect(); } - _parser = new TdsParser(ConnectionOptions.MARS, ConnectionOptions.Asynchronous); + _parser = new TdsParser(ConnectionOptions.MARS, connectionOptions.Asynchronous); + Debug.Assert(SniContext.Undefined == Parser._physicalStateObj.SniContext, $"SniContext should be Undefined; actual Value: {Parser._physicalStateObj.SniContext}"); currentServerInfo = new ServerInfo(ConnectionOptions, RoutingInfo, currentServerInfo.ResolvedServerName, currentServerInfo.ServerSPN); @@ -1970,9 +1971,9 @@ TimeoutTimer timeout _routingDestination = currentServerInfo.UserServerName; // restore properties that could be changed by the environment tokens - _currentPacketSize = ConnectionOptions.PacketSize; + _currentPacketSize = connectionOptions.PacketSize; _currentLanguage = _originalLanguage = ConnectionOptions.CurrentLanguage; - CurrentDatabase = _originalDatabase = ConnectionOptions.InitialCatalog; + CurrentDatabase = _originalDatabase = connectionOptions.InitialCatalog; _currentFailoverPartner = null; _instanceName = string.Empty; From a05742e4c7504bc7b335683ad403620f22a04600 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 07:31:40 +0100 Subject: [PATCH 14/18] netcore: changed MultiSubnetFailover timeout logic to more closely align with netfx --- .../SqlClient/SqlInternalConnectionTds.cs | 27 ++++++++++++++----- .../SqlClient/SqlInternalConnectionTds.cs | 8 +++--- 2 files changed, 24 insertions(+), 11 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 417417cf67..1bac52f6a9 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 @@ -1550,16 +1550,20 @@ private void LoginNoFailover(ServerInfo serverInfo, long timeoutUnitInterval = 0; - if (connectionOptions.MultiSubnetFailover) + bool isParallel = connectionOptions.MultiSubnetFailover; + + if (isParallel) { + float failoverTimeoutStep = ADP.FailoverTimeoutStep; + // Determine unit interval if (timeout.IsInfinite) { - timeoutUnitInterval = checked((long)(ADP.FailoverTimeoutStep * (1000L * ADP.DefaultConnectionTimeout))); + timeoutUnitInterval = checked((long)(failoverTimeoutStep * (1000L * ADP.DefaultConnectionTimeout))); } else { - timeoutUnitInterval = checked((long)(ADP.FailoverTimeoutStep * timeout.MillisecondsRemaining)); + timeoutUnitInterval = checked((long)(failoverTimeoutStep * timeout.MillisecondsRemaining)); } } // Only three ways out of this loop: @@ -1573,14 +1577,18 @@ private void LoginNoFailover(ServerInfo serverInfo, // back into the parser for the error cases. int attemptNumber = 0; TimeoutTimer intervalTimer = null; + TimeoutTimer attemptOneLoginTimeout = timeout; + while (true) { - if (connectionOptions.MultiSubnetFailover) + if (isParallel) { - attemptNumber++; + int multiplier = ++attemptNumber; + // Set timeout for this attempt, but don't exceed original timer - long nextTimeoutInterval = checked(timeoutUnitInterval * attemptNumber); + long nextTimeoutInterval = checked(timeoutUnitInterval * multiplier); long milliseconds = timeout.MillisecondsRemaining; + if (nextTimeoutInterval > milliseconds) { nextTimeoutInterval = milliseconds; @@ -1598,10 +1606,15 @@ private void LoginNoFailover(ServerInfo serverInfo, try { + if (isParallel) + { + attemptOneLoginTimeout = intervalTimer; + } + AttemptOneLogin(serverInfo, newPassword, newSecurePassword, - connectionOptions.MultiSubnetFailover ? intervalTimer : timeout); + attemptOneLoginTimeout); if (connectionOptions.MultiSubnetFailover && ServerProvidedFailOverPartner != null) { 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 5106fd6f68..a4b44b0e4e 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 @@ -1582,16 +1582,16 @@ private void LoginNoFailover(ServerInfo serverInfo, ResolveExtendedServerName(serverInfo, !redirectedUserInstance, connectionOptions); - Boolean disableTnir = ShouldDisableTnir(connectionOptions); + bool disableTnir = ShouldDisableTnir(connectionOptions); long timeoutUnitInterval = 0; - Boolean isParallel = connectionOptions.MultiSubnetFailover || (connectionOptions.TransparentNetworkIPResolution && !disableTnir); - + bool isParallel = connectionOptions.MultiSubnetFailover || (connectionOptions.TransparentNetworkIPResolution && !disableTnir); if (isParallel) { float failoverTimeoutStep = connectionOptions.MultiSubnetFailover ? ADP.FailoverTimeoutStep : ADP.FailoverTimeoutStepForTnir; + // Determine unit interval if (timeout.IsInfinite) { @@ -1613,8 +1613,8 @@ private void LoginNoFailover(ServerInfo serverInfo, // back into the parser for the error cases. int attemptNumber = 0; TimeoutTimer intervalTimer = null; - TimeoutTimer attemptOneLoginTimeout = timeout; + while (true) { Boolean isFirstTransparentAttempt = connectionOptions.TransparentNetworkIPResolution && !disableTnir && attemptNumber == 1; From 5a9d2b4e1ccf622788f0a8a8c6deb9e2928f0e93 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 07:46:20 +0100 Subject: [PATCH 15/18] netfx: align constructor parameter order with netcore --- .../src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs index d3402a48bd..4c0580e4d4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs @@ -145,7 +145,7 @@ override protected DbConnectionInternal CreateConnection(DbConnectionOptions opt opt = new SqlConnectionString(opt, instanceName, false /* user instance=false */, null /* do not modify the Enlist value */); poolGroupProviderInfo = null; // null so we do not pass to constructor below... } - result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, pool, key.AccessToken, applyTransientFaultHandling: applyTransientFaultHandling, key.AccessTokenCallback); + result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, applyTransientFaultHandling: applyTransientFaultHandling, key.AccessToken, pool, key.AccessTokenCallback); } return result; } 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 a4b44b0e4e..15bbd9bb1c 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 @@ -463,9 +463,9 @@ internal SqlInternalConnectionTds( bool redirectedUserInstance, SqlConnectionString userConnectionOptions = null, // NOTE: userConnectionOptions may be different to connectionOptions if the connection string has been expanded (see SqlConnectionString.Expand) SessionData reconnectSessionData = null, - DbConnectionPool pool = null, - string accessToken = null, bool applyTransientFaultHandling = false, + string accessToken = null, + DbConnectionPool pool = null, Func> accessTokenCallback = null) : base(connectionOptions) { From 37bcad52c85ed390cb3403ba0801975558f5eb3c Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 07:49:47 +0100 Subject: [PATCH 16/18] netfx: reorder case statement for global transactions feature --- .../SqlClient/SqlInternalConnectionTds.cs | 2 + .../SqlClient/SqlInternalConnectionTds.cs | 37 ++++++++++--------- 2 files changed, 21 insertions(+), 18 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 1bac52f6a9..e8511d40fa 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 @@ -2759,6 +2759,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) case TdsEnums.FEATUREEXT_GLOBALTRANSACTIONS: { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for GlobalTransactions", ObjectID); + if (data.Length < 1) { SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown version number for GlobalTransactions", ObjectID); @@ -2772,6 +2773,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) } break; } + case TdsEnums.FEATUREEXT_FEDAUTH: { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for federated authentication", ObjectID); 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 15bbd9bb1c..48584b6166 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 @@ -2834,6 +2834,25 @@ internal void OnFeatureExtAck(int featureId, byte[] data) } break; } + + case TdsEnums.FEATUREEXT_GLOBALTRANSACTIONS: + { + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for GlobalTransactions", ObjectID); + + if (data.Length < 1) + { + SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown version number for GlobalTransactions", ObjectID); + throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); + } + + IsGlobalTransaction = true; + if (1 == data[0]) + { + IsGlobalTransactionsEnabledForServer = true; + } + break; + } + case TdsEnums.FEATUREEXT_FEDAUTH: { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for federated authentication", ObjectID); @@ -2922,24 +2941,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) break; } - case TdsEnums.FEATUREEXT_GLOBALTRANSACTIONS: - { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for GlobalTransactions", ObjectID); - - if (data.Length < 1) - { - SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown version number for GlobalTransactions", ObjectID); - throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); - } - - IsGlobalTransaction = true; - if (1 == data[0]) - { - IsGlobalTransactionsEnabledForServer = true; - } - break; - } - case TdsEnums.FEATUREEXT_AZURESQLSUPPORT: { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for AzureSQLSupport", ObjectID); From 71b8cd747dc7a5129a4633744309ce4c3e9d9096 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 1 May 2025 22:05:05 +0100 Subject: [PATCH 17/18] Copy CAS to netcore --- .../SqlClient/SqlInternalConnectionTds.cs | 45 ++++++++++++++++++- .../SqlClient/SqlInternalConnectionTds.cs | 6 +-- 2 files changed, 45 insertions(+), 6 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 e8511d40fa..2f78f343d4 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 @@ -463,6 +463,26 @@ internal SqlInternalConnectionTds( { reconnectSessionData._debugReconnectDataApplied = true; } +#if NETFRAMEWORK + try + { + // use this to help validate this object is only created after the following permission has been previously demanded in the current codepath + if (userConnectionOptions != null) + { + // As mentioned above, userConnectionOptions may be different to connectionOptions, so we need to demand on the correct connection string + userConnectionOptions.DemandPermission(); + } + else + { + connectionOptions.DemandPermission(); + } + } + catch (System.Security.SecurityException) + { + System.Diagnostics.Debug.Assert(false, "unexpected SecurityException for current codepath"); + throw; + } +#endif #endif Debug.Assert(reconnectSessionData == null || connectionOptions.ConnectRetryCount > 0, "Reconnect data supplied with CR turned off"); @@ -906,6 +926,10 @@ internal override bool IsConnectionAlive(bool throwOnException) => protected override void Activate(Transaction transaction) { +#if NETFRAMEWORK + FailoverPermissionDemand(); // Demand for unspecified failover pooled connections +#endif + // When we're required to automatically enlist in transactions and // there is one we enlist in it. On the other hand, if there isn't a // transaction and we are currently enlisted in one, then we @@ -1799,6 +1823,7 @@ TimeoutTimer timeout } // Initialize loop variables + bool failoverDemandDone = false; // have we demanded for partner information yet (as necessary)? int attemptNumber = 0; // Only three ways out of this loop: @@ -1833,6 +1858,14 @@ TimeoutTimer timeout ServerInfo currentServerInfo; if (useFailoverHost) { + if (!failoverDemandDone) + { +#if NETFRAMEWORK + FailoverPermissionDemand(); +#endif + failoverDemandDone = true; + } + // Primary server may give us a different failover partner than the connection string indicates. Update it if (ServerProvidedFailOverPartner != null && failoverServerInfo.ResolvedServerName != ServerProvidedFailOverPartner) { @@ -2040,6 +2073,16 @@ private void AttemptOneLogin(ServerInfo serverInfo, _timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.PostLogin); } +#if NETFRAMEWORK + internal void FailoverPermissionDemand() + { + if (PoolGroupProviderInfo != null) + { + PoolGroupProviderInfo.FailoverPermissionDemand(); + } + } +#endif + //////////////////////////////////////////////////////////////////////////////////////// // PREPARED COMMAND METHODS //////////////////////////////////////////////////////////////////////////////////////// @@ -2828,7 +2871,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) } #endif } - break; } case TdsEnums.FEATUREEXT_TCE: @@ -2949,7 +2991,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) // get IPv4 + IPv6 + Port number // not put them in the DNS cache at this point but need to store them somewhere // generate pendingSQLDNSObject and turn on IsSQLDNSRetryEnabled flag - break; } 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 48584b6166..df257c5467 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 @@ -475,7 +475,8 @@ internal SqlInternalConnectionTds( reconnectSessionData._debugReconnectDataApplied = true; } try - { // use this to help validate this object is only created after the following permission has been previously demanded in the current codepath + { + // use this to help validate this object is only created after the following permission has been previously demanded in the current codepath if (userConnectionOptions != null) { // As mentioned above, userConnectionOptions may be different to connectionOptions, so we need to demand on the correct connection string @@ -2907,7 +2908,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) } #endif } - break; } case TdsEnums.FEATUREEXT_TCE: @@ -2937,7 +2937,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) // Extract the type of enclave being used by the server. _parser.EnclaveType = Encoding.Unicode.GetString(data, 2, (data.Length - 2)); } - break; } @@ -3029,7 +3028,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) // get IPv4 + IPv6 + Port number // not put them in the DNS cache at this point but need to store them somewhere // generate pendingSQLDNSObject and turn on IsSQLDNSRetryEnabled flag - break; } From 9fda691aba2b804c20af596473880f714e836304 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 6 May 2025 18:34:28 +0100 Subject: [PATCH 18/18] First round of code review --- .../Data/SqlClient/SqlConnectionFactory.cs | 2 +- .../SqlClient/SqlInternalConnectionTds.cs | 21 +++++++++---------- .../Data/SqlClient/SqlConnectionFactory.cs | 2 +- .../SqlClient/SqlInternalConnectionTds.cs | 21 +++++++++---------- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs index ca9bcc2cd9..bfda03ae42 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs @@ -136,7 +136,7 @@ override protected DbConnectionInternal CreateConnection(DbConnectionOptions opt opt = new SqlConnectionString(opt, instanceName, userInstance: false, setEnlistValue: null); poolGroupProviderInfo = null; // null so we do not pass to constructor below... } - return new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, applyTransientFaultHandling: applyTransientFaultHandling, key.AccessToken, pool, key.AccessTokenCallback); + return new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, applyTransientFaultHandling, key.AccessToken, pool, key.AccessTokenCallback); } protected override DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous) 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 2f78f343d4..7d6d2b51f6 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 @@ -106,14 +106,13 @@ internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposa { // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600 internal const int MsalHttpRetryStatusCode = 429; + // Connection re-route limit + internal const int MaxNumberOfRedirectRoute = 10; // CONNECTION AND STATE VARIABLES private readonly SqlConnectionPoolGroupProviderInfo _poolGroupProviderInfo; // will only be null when called for ChangePassword, or creating SSE User Instance private TdsParser _parser; - // Connection re-route limit - internal const int _maxNumberOfRedirectRoute = 10; - private SqlLoginAck _loginAck; private SqlCredential _credential; private FederatedAuthenticationFeatureExtensionData _fedAuthFeatureExtensionData; @@ -186,7 +185,7 @@ internal bool IsSQLDNSRetryEnabled } } - private bool _DNSCachingBeforeRedirect = false; + private bool _dnsCachingBeforeRedirect = false; /// /// Get or set if the control ring send redirect token and feature ext ack with true for DNSCaching @@ -195,11 +194,11 @@ internal bool IsDNSCachingBeforeRedirectSupported { get { - return _DNSCachingBeforeRedirect; + return _dnsCachingBeforeRedirect; } set { - _DNSCachingBeforeRedirect = value; + _dnsCachingBeforeRedirect = value; } } @@ -1649,9 +1648,9 @@ private void LoginNoFailover(ServerInfo serverInfo, if (RoutingInfo != null) { SqlClientEventSource.Log.TryTraceEvent(" Routed to {0}", serverInfo.ExtendedServerName); - if (routingAttempts > _maxNumberOfRedirectRoute) + if (routingAttempts > MaxNumberOfRedirectRoute) { - throw SQL.ROR_RecursiveRoutingNotSupported(this, _maxNumberOfRedirectRoute); + throw SQL.ROR_RecursiveRoutingNotSupported(this, MaxNumberOfRedirectRoute); } if (timeout.IsExpired) @@ -1895,9 +1894,9 @@ TimeoutTimer timeout int routingAttempts = 0; while (RoutingInfo != null) { - if (routingAttempts > _maxNumberOfRedirectRoute) + if (routingAttempts > MaxNumberOfRedirectRoute) { - throw SQL.ROR_RecursiveRoutingNotSupported(this, _maxNumberOfRedirectRoute); + throw SQL.ROR_RecursiveRoutingNotSupported(this, MaxNumberOfRedirectRoute); } routingAttempts++; @@ -2742,7 +2741,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) internal void OnFeatureExtAck(int featureId, byte[] data) { - if (RoutingInfo != null && TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) + if (RoutingInfo != null && featureId != TdsEnums.FEATUREEXT_SQLDNSCACHING) { return; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs index 4c0580e4d4..12a6378ddc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs @@ -145,7 +145,7 @@ override protected DbConnectionInternal CreateConnection(DbConnectionOptions opt opt = new SqlConnectionString(opt, instanceName, false /* user instance=false */, null /* do not modify the Enlist value */); poolGroupProviderInfo = null; // null so we do not pass to constructor below... } - result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, applyTransientFaultHandling: applyTransientFaultHandling, key.AccessToken, pool, key.AccessTokenCallback); + result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, applyTransientFaultHandling, key.AccessToken, pool, key.AccessTokenCallback); } return result; } 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 df257c5467..5986736403 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 @@ -106,14 +106,13 @@ internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposa { // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600 internal const int MsalHttpRetryStatusCode = 429; + // Connection re-route limit + internal const int MaxNumberOfRedirectRoute = 10; // CONNECTION AND STATE VARIABLES private readonly SqlConnectionPoolGroupProviderInfo _poolGroupProviderInfo; // will only be null when called for ChangePassword, or creating SSE User Instance private TdsParser _parser; - // Connection re-route limit - internal const int _maxNumberOfRedirectRoute = 10; - private SqlLoginAck _loginAck; private SqlCredential _credential; private FederatedAuthenticationFeatureExtensionData _fedAuthFeatureExtensionData; @@ -186,7 +185,7 @@ internal bool IsSQLDNSRetryEnabled } } - private bool _DNSCachingBeforeRedirect = false; + private bool _dnsCachingBeforeRedirect = false; /// /// Get or set if the control ring send redirect token and feature ext ack with true for DNSCaching @@ -195,11 +194,11 @@ internal bool IsDNSCachingBeforeRedirectSupported { get { - return _DNSCachingBeforeRedirect; + return _dnsCachingBeforeRedirect; } set { - _DNSCachingBeforeRedirect = value; + _dnsCachingBeforeRedirect = value; } } @@ -1682,9 +1681,9 @@ private void LoginNoFailover(ServerInfo serverInfo, if (RoutingInfo != null) { SqlClientEventSource.Log.TryTraceEvent(" Routed to {0}", serverInfo.ExtendedServerName); - if (routingAttempts > _maxNumberOfRedirectRoute) + if (routingAttempts > MaxNumberOfRedirectRoute) { - throw SQL.ROR_RecursiveRoutingNotSupported(this, _maxNumberOfRedirectRoute); + throw SQL.ROR_RecursiveRoutingNotSupported(this, MaxNumberOfRedirectRoute); } if (timeout.IsExpired) @@ -1949,9 +1948,9 @@ TimeoutTimer timeout int routingAttempts = 0; while (RoutingInfo != null) { - if (routingAttempts > _maxNumberOfRedirectRoute) + if (routingAttempts > MaxNumberOfRedirectRoute) { - throw SQL.ROR_RecursiveRoutingNotSupported(this, _maxNumberOfRedirectRoute); + throw SQL.ROR_RecursiveRoutingNotSupported(this, MaxNumberOfRedirectRoute); } routingAttempts++; @@ -2779,7 +2778,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) internal void OnFeatureExtAck(int featureId, byte[] data) { - if (RoutingInfo != null && TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) + if (RoutingInfo != null && featureId != TdsEnums.FEATUREEXT_SQLDNSCACHING) { return; }