From f777bd8495891d6e905c6895c914688acf757a78 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 1 Oct 2025 06:20:02 +0100
Subject: [PATCH 01/25] Move TdsParserStateObjectManaged.cs
---
.../netcore/src/Microsoft.Data.SqlClient.csproj | 4 +++-
.../Data/SqlClient/TdsParserStateObjectManaged.netcore.cs} | 0
2 files changed, 3 insertions(+), 1 deletion(-)
rename src/Microsoft.Data.SqlClient/{netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs => src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.netcore.cs} (100%)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index 5c67ef9d5b..5d06d69703 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -783,6 +783,9 @@
Microsoft\Data\SqlClient\TdsParserStateObject.Multiplexer.cs
+
+ Microsoft\Data\SqlClient\TdsParserStateObjectManaged.netcore.cs
+
Microsoft\Data\SqlClient\TdsParserStaticMethods.cs
@@ -835,7 +838,6 @@
-
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.netcore.cs
similarity index 100%
rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs
rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.netcore.cs
From 06060c9e6cacd531cea06cbb70771522371a8698 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 1 Oct 2025 06:26:55 +0100
Subject: [PATCH 02/25] Merge netfx-only variables
---
.../Data/SqlClient/TdsParserStateObject.netfx.cs | 6 ------
.../src/Microsoft/Data/SqlClient/TdsParserStateObject.cs | 8 ++++++++
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
index 194beaca5f..2d0a6d9c17 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
@@ -16,12 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal partial class TdsParserStateObject
{
- // Used for blanking out password in trace.
- internal int _tracePasswordOffset = 0;
- internal int _tracePasswordLength = 0;
- internal int _traceChangePasswordOffset = 0;
- internal int _traceChangePasswordLength = 0;
-
//////////////////
// Constructors //
//////////////////
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
index 967c70250a..1f2e8b741d 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -290,6 +290,14 @@ private enum SnapshotStatus
// as the error handling may end up calling Dispose.
private int _readingCount;
+#if NETFRAMEWORK
+ // Used for blanking out password in trace.
+ internal int _tracePasswordOffset = 0;
+ internal int _tracePasswordLength = 0;
+ internal int _traceChangePasswordOffset = 0;
+ internal int _traceChangePasswordLength = 0;
+#endif
+
// Test hooks
#if DEBUG
// This is a test hook to enable testing of the retry paths.
From 1c73f139fdf3c176b5168764a52f40075a92e7a6 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 1 Oct 2025 06:29:07 +0100
Subject: [PATCH 03/25] Merge constructor
---
.../SqlClient/TdsParserStateObject.netcore.cs | 36 -------------------
.../SqlClient/TdsParserStateObject.netfx.cs | 36 -------------------
.../Data/SqlClient/TdsParserStateObject.cs | 32 +++++++++++++++++
3 files changed, 32 insertions(+), 72 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
index 9111a19eec..3065b29ee6 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
@@ -16,42 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal abstract partial class TdsParserStateObject
{
- //////////////////
- // Constructors //
- //////////////////
-
- protected TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalConnection, bool async)
- {
- // Construct a MARS session
- Debug.Assert(parser != null, "no parser?");
- _parser = parser;
- _onTimeoutAsync = OnTimeoutAsync;
- SniContext = SniContext.Snix_GetMarsSession;
-
- Debug.Assert(_parser._physicalStateObj != null, "no physical session?");
- Debug.Assert(_parser._physicalStateObj._inBuff != null, "no in buffer?");
- Debug.Assert(_parser._physicalStateObj._outBuff != null, "no out buffer?");
- Debug.Assert(_parser._physicalStateObj._outBuff.Length ==
- _parser._physicalStateObj._inBuff.Length, "Unexpected unequal buffers.");
-
- // Determine packet size based on physical connection buffer lengths.
- SetPacketSize(_parser._physicalStateObj._outBuff.Length);
-
- CreateSessionHandle(physicalConnection, async);
-
- if (IsFailedHandle())
- {
- AddError(parser.ProcessSNIError(this));
- ThrowExceptionAndWarning();
- }
-
- // we post a callback that represents the call to dispose; once the
- // object is disposed, the next callback will cause the GC Handle to
- // be released.
- IncrementPendingCallbacks();
- _lastSuccessfulIOTimer = parser._physicalStateObj._lastSuccessfulIOTimer;
- }
-
/////////////////////
// General methods //
/////////////////////
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
index 2d0a6d9c17..7eafc291a1 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
@@ -16,42 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal partial class TdsParserStateObject
{
- //////////////////
- // Constructors //
- //////////////////
-
- protected TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalConnection, bool async)
- {
- // Construct a MARS session
- Debug.Assert(parser != null, "no parser?");
- _parser = parser;
- _onTimeoutAsync = OnTimeoutAsync;
- SniContext = SniContext.Snix_GetMarsSession;
-
- Debug.Assert(_parser._physicalStateObj != null, "no physical session?");
- Debug.Assert(_parser._physicalStateObj._inBuff != null, "no in buffer?");
- Debug.Assert(_parser._physicalStateObj._outBuff != null, "no out buffer?");
- Debug.Assert(_parser._physicalStateObj._outBuff.Length ==
- _parser._physicalStateObj._inBuff.Length, "Unexpected unequal buffers.");
-
- // Determine packet size based on physical connection buffer lengths.
- SetPacketSize(_parser._physicalStateObj._outBuff.Length);
-
- CreateSessionHandle(physicalConnection, async);
-
- if (IsFailedHandle())
- {
- AddError(parser.ProcessSNIError(this));
- ThrowExceptionAndWarning();
- }
-
- // we post a callback that represents the call to dispose; once the
- // object is disposed, the next callback will cause the GC Handle to
- // be released.
- IncrementPendingCallbacks();
- _lastSuccessfulIOTimer = parser._physicalStateObj._lastSuccessfulIOTimer;
- }
-
/////////////////////
// General methods //
/////////////////////
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
index 1f2e8b741d..d3e435cd3e 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -360,6 +360,38 @@ protected TdsParserStateObject(TdsParser parser)
_lastSuccessfulIOTimer = new LastIOTimer();
}
+ protected TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalConnection, bool async)
+ {
+ // Construct a MARS session
+ Debug.Assert(parser != null, "no parser?");
+ _parser = parser;
+ _onTimeoutAsync = OnTimeoutAsync;
+ SniContext = SniContext.Snix_GetMarsSession;
+
+ Debug.Assert(_parser._physicalStateObj != null, "no physical session?");
+ Debug.Assert(_parser._physicalStateObj._inBuff != null, "no in buffer?");
+ Debug.Assert(_parser._physicalStateObj._outBuff != null, "no out buffer?");
+ Debug.Assert(_parser._physicalStateObj._outBuff.Length ==
+ _parser._physicalStateObj._inBuff.Length, "Unexpected unequal buffers.");
+
+ // Determine packet size based on physical connection buffer lengths.
+ SetPacketSize(_parser._physicalStateObj._outBuff.Length);
+
+ CreateSessionHandle(physicalConnection, async);
+
+ if (IsFailedHandle())
+ {
+ AddError(parser.ProcessSNIError(this));
+ ThrowExceptionAndWarning();
+ }
+
+ // we post a callback that represents the call to dispose; once the
+ // object is disposed, the next callback will cause the GC Handle to
+ // be released.
+ IncrementPendingCallbacks();
+ _lastSuccessfulIOTimer = parser._physicalStateObj._lastSuccessfulIOTimer;
+ }
+
private void SetSnapshottedState(SnapshottedStateFlags flag, bool value)
{
if (value)
From b8bfc28dc88bf5e0c2b9d16637e4fcf221b43378 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 1 Oct 2025 06:32:51 +0100
Subject: [PATCH 04/25] netfx: sync trace string
---
.../src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
index 7eafc291a1..4ded61d499 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
@@ -23,7 +23,7 @@ internal partial class TdsParserStateObject
internal int DecrementPendingCallbacks(bool release)
{
int remaining = Interlocked.Decrement(ref _pendingCallbacks);
- SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, after decrementing _pendingCallbacks: {1}", ObjectID, _pendingCallbacks);
+ SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObject.DecrementPendingCallbacks | ADV | State Object Id {0}, after decrementing _pendingCallbacks: {1}", _objectID, _pendingCallbacks);
FreeGcHandle(remaining, release);
From 3779e3b7841cf56ced654d228940ab47e0412d66 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 1 Oct 2025 06:34:19 +0100
Subject: [PATCH 05/25] Merge DecrementPendingCallbacks
---
.../SqlClient/TdsParserStateObject.netcore.cs | 17 -----------------
.../SqlClient/TdsParserStateObject.netfx.cs | 17 -----------------
.../Data/SqlClient/TdsParserStateObject.cs | 13 +++++++++++++
3 files changed, 13 insertions(+), 34 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
index 3065b29ee6..70d57919ce 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
@@ -16,23 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal abstract partial class TdsParserStateObject
{
- /////////////////////
- // General methods //
- /////////////////////
-
- internal int DecrementPendingCallbacks(bool release)
- {
- int remaining = Interlocked.Decrement(ref _pendingCallbacks);
- SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObject.DecrementPendingCallbacks | ADV | State Object Id {0}, after decrementing _pendingCallbacks: {1}", _objectID, _pendingCallbacks);
-
- FreeGcHandle(remaining, release);
-
- // NOTE: TdsParserSessionPool may call DecrementPendingCallbacks on a TdsParserStateObject which is already disposed
- // This is not dangerous (since the stateObj is no longer in use), but we need to add a workaround in the assert for it
- Debug.Assert((remaining == -1 && SessionHandle.IsNull) || (0 <= remaining && remaining < 3), $"_pendingCallbacks values is invalid after decrementing: {remaining}");
- return remaining;
- }
-
///
/// Checks to see if the underlying connection is still valid (used by idle connection resiliency - for active connections)
/// NOTE: This is not safe to do on a connection that is currently in use
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
index 4ded61d499..6e2b1ce528 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
@@ -16,23 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal partial class TdsParserStateObject
{
- /////////////////////
- // General methods //
- /////////////////////
-
- internal int DecrementPendingCallbacks(bool release)
- {
- int remaining = Interlocked.Decrement(ref _pendingCallbacks);
- SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObject.DecrementPendingCallbacks | ADV | State Object Id {0}, after decrementing _pendingCallbacks: {1}", _objectID, _pendingCallbacks);
-
- FreeGcHandle(remaining, release);
-
- // NOTE: TdsParserSessionPool may call DecrementPendingCallbacks on a TdsParserStateObject which is already disposed
- // This is not dangerous (since the stateObj is no longer in use), but we need to add a workaround in the assert for it
- Debug.Assert((remaining == -1 && SessionHandle.IsNull) || (0 <= remaining && remaining < 3), $"_pendingCallbacks values is invalid after decrementing: {remaining}");
- return remaining;
- }
-
///
/// Checks to see if the underlying connection is still valid (used by idle connection resiliency - for active connections)
/// NOTE: This is not safe to do on a connection that is currently in use
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
index d3e435cd3e..3d7a645d5d 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -1014,6 +1014,19 @@ internal int IncrementPendingCallbacks()
return remaining;
}
+ internal int DecrementPendingCallbacks(bool release)
+ {
+ int remaining = Interlocked.Decrement(ref _pendingCallbacks);
+ SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObject.DecrementPendingCallbacks | ADV | State Object Id {0}, after decrementing _pendingCallbacks: {1}", _objectID, _pendingCallbacks);
+
+ FreeGcHandle(remaining, release);
+
+ // NOTE: TdsParserSessionPool may call DecrementPendingCallbacks on a TdsParserStateObject which is already disposed
+ // This is not dangerous (since the stateObj is no longer in use), but we need to add a workaround in the assert for it
+ Debug.Assert((remaining == -1 && SessionHandle.IsNull) || (0 <= remaining && remaining < 3), $"_pendingCallbacks values is invalid after decrementing: {remaining}");
+ return remaining;
+ }
+
internal bool Deactivate()
{
bool goodForReuse = false;
From 1910cb8842890fb2fbc0d3be90c896c1ff8915b3 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 1 Oct 2025 06:37:23 +0100
Subject: [PATCH 06/25] Merge ValidateSNIConnection
---
.../SqlClient/TdsParserStateObject.netcore.cs | 32 -------------------
.../SqlClient/TdsParserStateObject.netfx.cs | 32 -------------------
.../Data/SqlClient/TdsParserStateObject.cs | 32 +++++++++++++++++++
3 files changed, 32 insertions(+), 64 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
index 70d57919ce..55c6240131 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
@@ -16,38 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal abstract partial class TdsParserStateObject
{
- ///
- /// Checks to see if the underlying connection is still valid (used by idle connection resiliency - for active connections)
- /// NOTE: This is not safe to do on a connection that is currently in use
- /// NOTE: This will mark the connection as broken if it is found to be dead
- ///
- /// True if the connection is still alive, otherwise false
- internal bool ValidateSNIConnection()
- {
- if ((_parser == null) || ((_parser.State == TdsParserState.Broken) || (_parser.State == TdsParserState.Closed)))
- {
- return false;
- }
-
- if (DateTime.UtcNow.Ticks - _lastSuccessfulIOTimer._value <= CheckConnectionWindow)
- {
- return true;
- }
-
- uint error = TdsEnums.SNI_SUCCESS;
- SniContext = SniContext.Snix_Connect;
- try
- {
- Interlocked.Increment(ref _readingCount);
- error = CheckConnection();
- }
- finally
- {
- Interlocked.Decrement(ref _readingCount);
- }
- return (error == TdsEnums.SNI_SUCCESS) || (error == TdsEnums.SNI_WAIT_TIMEOUT);
- }
-
// This method should only be called by ReadSni! If not - it may have problems with timeouts!
private void ReadSniError(TdsParserStateObject stateObj, uint error)
{
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
index 6e2b1ce528..8128a6d783 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
@@ -16,38 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal partial class TdsParserStateObject
{
- ///
- /// Checks to see if the underlying connection is still valid (used by idle connection resiliency - for active connections)
- /// NOTE: This is not safe to do on a connection that is currently in use
- /// NOTE: This will mark the connection as broken if it is found to be dead
- ///
- /// True if the connection is still alive, otherwise false
- internal bool ValidateSNIConnection()
- {
- if ((_parser == null) || ((_parser.State == TdsParserState.Broken) || (_parser.State == TdsParserState.Closed)))
- {
- return false;
- }
-
- if (DateTime.UtcNow.Ticks - _lastSuccessfulIOTimer._value <= CheckConnectionWindow)
- {
- return true;
- }
-
- uint error = TdsEnums.SNI_SUCCESS;
- SniContext = SniContext.Snix_Connect;
- try
- {
- Interlocked.Increment(ref _readingCount);
- error = CheckConnection();
- }
- finally
- {
- Interlocked.Decrement(ref _readingCount);
- }
- return (error == TdsEnums.SNI_SUCCESS) || (error == TdsEnums.SNI_WAIT_TIMEOUT);
- }
-
// This method should only be called by ReadSni! If not - it may have problems with timeouts!
private void ReadSniError(TdsParserStateObject stateObj, uint error)
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
index 3d7a645d5d..e514c6888c 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -951,6 +951,38 @@ internal void CancelRequest()
}
}
+ ///
+ /// Checks to see if the underlying connection is still valid (used by idle connection resiliency - for active connections)
+ /// NOTE: This is not safe to do on a connection that is currently in use
+ /// NOTE: This will mark the connection as broken if it is found to be dead
+ ///
+ /// True if the connection is still alive, otherwise false
+ internal bool ValidateSNIConnection()
+ {
+ if ((_parser == null) || ((_parser.State == TdsParserState.Broken) || (_parser.State == TdsParserState.Closed)))
+ {
+ return false;
+ }
+
+ if (DateTime.UtcNow.Ticks - _lastSuccessfulIOTimer._value <= CheckConnectionWindow)
+ {
+ return true;
+ }
+
+ uint error = TdsEnums.SNI_SUCCESS;
+ SniContext = SniContext.Snix_Connect;
+ try
+ {
+ Interlocked.Increment(ref _readingCount);
+ error = CheckConnection();
+ }
+ finally
+ {
+ Interlocked.Decrement(ref _readingCount);
+ }
+ return (error == TdsEnums.SNI_SUCCESS) || (error == TdsEnums.SNI_WAIT_TIMEOUT);
+ }
+
public void CheckSetResetConnectionState(uint error, CallbackType callbackType)
{
// Should only be called for MARS - that is the only time we need to take
From 0228eb3674c98c12aab373dd20bca8901d97cbaa Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 1 Oct 2025 06:40:31 +0100
Subject: [PATCH 07/25] netfx, netcore: sync TransparentNetworkIPResolution
error handling
---
.../Data/SqlClient/TdsParserStateObject.netcore.cs | 7 ++++++-
.../Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs | 7 ++++++-
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
index 55c6240131..8ab4e320f7 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
@@ -93,7 +93,12 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
// For DbMirroring Failover during login, never break the connection, just close the TdsParser
_parser.Disconnect();
}
- else if ((_parser.State == TdsParserState.OpenNotLoggedIn) && (_parser.Connection.ConnectionOptions.MultiSubnetFailover))
+ else if ((_parser.State == TdsParserState.OpenNotLoggedIn)
+#if NETFRAMEWORK
+ && (_parser.Connection.ConnectionOptions.MultiSubnetFailover || _parser.Connection.ConnectionOptions.TransparentNetworkIPResolution))
+#else
+ && (_parser.Connection.ConnectionOptions.MultiSubnetFailover))
+#endif
{
// For MultiSubnet Failover during login, never break the connection, just close the TdsParser
_parser.Disconnect();
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
index 8128a6d783..b681c1abec 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
@@ -93,7 +93,12 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
// For DbMirroring Failover during login, never break the connection, just close the TdsParser
_parser.Disconnect();
}
- else if ((_parser.State == TdsParserState.OpenNotLoggedIn) && (_parser.Connection.ConnectionOptions.MultiSubnetFailover || _parser.Connection.ConnectionOptions.TransparentNetworkIPResolution))
+ else if ((_parser.State == TdsParserState.OpenNotLoggedIn)
+#if NETFRAMEWORK
+ && (_parser.Connection.ConnectionOptions.MultiSubnetFailover || _parser.Connection.ConnectionOptions.TransparentNetworkIPResolution))
+#else
+ && (_parser.Connection.ConnectionOptions.MultiSubnetFailover))
+#endif
{
// For MultiSubnet Failover during login, never break the connection, just close the TdsParser
_parser.Disconnect();
From 8d82f4d296e0e0912b8969ebaa23e56df045eb8e Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 1 Oct 2025 06:46:27 +0100
Subject: [PATCH 08/25] Merge ReadSniError
---
.../SqlClient/TdsParserStateObject.netcore.cs | 109 ------------------
.../SqlClient/TdsParserStateObject.netfx.cs | 109 ------------------
.../Data/SqlClient/TdsParserStateObject.cs | 109 ++++++++++++++++++
3 files changed, 109 insertions(+), 218 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
index 8ab4e320f7..47d836134a 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
@@ -16,115 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal abstract partial class TdsParserStateObject
{
- // This method should only be called by ReadSni! If not - it may have problems with timeouts!
- private void ReadSniError(TdsParserStateObject stateObj, uint error)
- {
- if (TdsEnums.SNI_WAIT_TIMEOUT == error)
- {
- Debug.Assert(_syncOverAsync, "Should never reach here with async on!");
- bool fail = false;
-
- if (IsTimeoutStateExpired)
- { // This is now our second timeout - time to give up.
- fail = true;
- }
- else
- {
- stateObj.SetTimeoutStateStopped();
- Debug.Assert(_parser.Connection != null, "SqlConnectionInternalTds handler can not be null at this point.");
- AddError(new SqlError(TdsEnums.TIMEOUT_EXPIRED, 0x00, TdsEnums.MIN_ERROR_CLASS, _parser.Server, _parser.Connection.TimeoutErrorInternal.GetErrorMessage(), "", 0, TdsEnums.SNI_WAIT_TIMEOUT));
-
- if (!stateObj._attentionSent)
- {
- if (stateObj.Parser.State == TdsParserState.OpenLoggedIn)
- {
- stateObj.SendAttention(mustTakeWriteLock: true);
-
- PacketHandle syncReadPacket = default;
- bool readFromNetwork = true;
- bool shouldDecrement = false;
- try
- {
- Interlocked.Increment(ref _readingCount);
- shouldDecrement = true;
- readFromNetwork = !PartialPacketContainsCompletePacket();
- if (readFromNetwork)
- {
- syncReadPacket = ReadSyncOverAsync(stateObj.GetTimeoutRemaining(), out error);
- }
- else
- {
- error = TdsEnums.SNI_SUCCESS;
- }
-
- Interlocked.Decrement(ref _readingCount);
- shouldDecrement = false;
-
- if (TdsEnums.SNI_SUCCESS == error)
- {
- // We will end up letting the run method deal with the expected done:done_attn token stream.
- stateObj.ProcessSniPacket(syncReadPacket, TdsEnums.SNI_SUCCESS);
- return;
- }
- else
- {
- Debug.Assert(!readFromNetwork || !IsValidPacket(syncReadPacket), "unexpected syncReadPacket without corresponding SNIPacketRelease");
- fail = true; // Subsequent read failed, time to give up.
- }
- }
- finally
- {
- if (shouldDecrement)
- {
- Interlocked.Decrement(ref _readingCount);
- }
-
- if (readFromNetwork && !IsPacketEmpty(syncReadPacket))
- {
- // Be sure to release packet, otherwise it will be leaked by native.
- ReleasePacket(syncReadPacket);
- }
- }
- }
- else
- {
- if (_parser._loginWithFailover)
- {
- // For DbMirroring Failover during login, never break the connection, just close the TdsParser
- _parser.Disconnect();
- }
- else if ((_parser.State == TdsParserState.OpenNotLoggedIn)
-#if NETFRAMEWORK
- && (_parser.Connection.ConnectionOptions.MultiSubnetFailover || _parser.Connection.ConnectionOptions.TransparentNetworkIPResolution))
-#else
- && (_parser.Connection.ConnectionOptions.MultiSubnetFailover))
-#endif
- {
- // For MultiSubnet Failover during login, never break the connection, just close the TdsParser
- _parser.Disconnect();
- }
- else
- fail = true; // We aren't yet logged in - just fail.
- }
- }
- }
-
- if (fail)
- {
- _parser.State = TdsParserState.Broken; // We failed subsequent read, we have to quit!
- _parser.Connection.BreakConnection();
- }
- }
- else
- {
- // Caution: ProcessSNIError always returns a fatal error!
- AddError(_parser.ProcessSNIError(stateObj));
- }
- ThrowExceptionAndWarning();
-
- AssertValidState();
- }
-
private uint GetSniPacket(PacketHandle packet, ref uint dataSize)
{
return SniPacketGetData(packet, _inBuff, ref dataSize);
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
index b681c1abec..c1e50fa308 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
@@ -16,115 +16,6 @@ namespace Microsoft.Data.SqlClient
{
internal partial class TdsParserStateObject
{
- // This method should only be called by ReadSni! If not - it may have problems with timeouts!
- private void ReadSniError(TdsParserStateObject stateObj, uint error)
- {
- if (TdsEnums.SNI_WAIT_TIMEOUT == error)
- {
- Debug.Assert(_syncOverAsync, "Should never reach here with async on!");
- bool fail = false;
-
- if (IsTimeoutStateExpired)
- { // This is now our second timeout - time to give up.
- fail = true;
- }
- else
- {
- stateObj.SetTimeoutStateStopped();
- Debug.Assert(_parser.Connection != null, "SqlConnectionInternalTds handler can not be null at this point.");
- AddError(new SqlError(TdsEnums.TIMEOUT_EXPIRED, 0x00, TdsEnums.MIN_ERROR_CLASS, _parser.Server, _parser.Connection.TimeoutErrorInternal.GetErrorMessage(), "", 0, TdsEnums.SNI_WAIT_TIMEOUT));
-
- if (!stateObj._attentionSent)
- {
- if (stateObj.Parser.State == TdsParserState.OpenLoggedIn)
- {
- stateObj.SendAttention(mustTakeWriteLock: true);
-
- PacketHandle syncReadPacket = default;
- bool readFromNetwork = true;
- bool shouldDecrement = false;
- try
- {
- Interlocked.Increment(ref _readingCount);
- shouldDecrement = true;
- readFromNetwork = !PartialPacketContainsCompletePacket();
- if (readFromNetwork)
- {
- syncReadPacket = ReadSyncOverAsync(stateObj.GetTimeoutRemaining(), out error);
- }
- else
- {
- error = TdsEnums.SNI_SUCCESS;
- }
-
- Interlocked.Decrement(ref _readingCount);
- shouldDecrement = false;
-
- if (TdsEnums.SNI_SUCCESS == error)
- {
- // We will end up letting the run method deal with the expected done:done_attn token stream.
- stateObj.ProcessSniPacket(syncReadPacket, TdsEnums.SNI_SUCCESS);
- return;
- }
- else
- {
- Debug.Assert(!readFromNetwork || !IsValidPacket(syncReadPacket), "unexpected syncReadPacket without corresponding SNIPacketRelease");
- fail = true; // Subsequent read failed, time to give up.
- }
- }
- finally
- {
- if (shouldDecrement)
- {
- Interlocked.Decrement(ref _readingCount);
- }
-
- if (readFromNetwork && !IsPacketEmpty(syncReadPacket))
- {
- // Be sure to release packet, otherwise it will be leaked by native.
- ReleasePacket(syncReadPacket);
- }
- }
- }
- else
- {
- if (_parser._loginWithFailover)
- {
- // For DbMirroring Failover during login, never break the connection, just close the TdsParser
- _parser.Disconnect();
- }
- else if ((_parser.State == TdsParserState.OpenNotLoggedIn)
-#if NETFRAMEWORK
- && (_parser.Connection.ConnectionOptions.MultiSubnetFailover || _parser.Connection.ConnectionOptions.TransparentNetworkIPResolution))
-#else
- && (_parser.Connection.ConnectionOptions.MultiSubnetFailover))
-#endif
- {
- // For MultiSubnet Failover during login, never break the connection, just close the TdsParser
- _parser.Disconnect();
- }
- else
- fail = true; // We aren't yet logged in - just fail.
- }
- }
- }
-
- if (fail)
- {
- _parser.State = TdsParserState.Broken; // We failed subsequent read, we have to quit!
- _parser.Connection.BreakConnection();
- }
- }
- else
- {
- // Caution: ProcessSNIError always returns a fatal error!
- AddError(_parser.ProcessSNIError(stateObj));
- }
- ThrowExceptionAndWarning();
-
- AssertValidState();
- }
-
private uint GetSniPacket(PacketHandle packet, ref uint dataSize)
{
return SniPacketGetData(packet, _inBuff, ref dataSize);
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
index e514c6888c..828202f556 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -3440,6 +3440,115 @@ internal void ReadSni(TaskCompletionSource