From c784c7faef38cedb934710b551fb5deeba9b00ab Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:04:46 -0500 Subject: [PATCH 01/21] Remove CER from sqlbulkcopy --- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 65 +++---------------- 1 file changed, 8 insertions(+), 57 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 80406c1e7e..11b3cd980f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -2064,14 +2064,8 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok _parserLock = internalConnection._parserLock; _parserLock.Wait(canReleaseFromAnyThread: _isAsyncBulkCopy); - TdsParser bestEffortCleanupTarget = null; - - #if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); - #endif try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); WriteRowSourceToServerCommon(columnCount); // This is common in both sync and async Task resultTask = WriteToServerInternalAsync(ctoken); // resultTask is null for sync, but Task for async. if (resultTask != null) @@ -2107,26 +2101,6 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok } return null; } - catch (System.OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _connection.Abort(e); - - #if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - #endif - - throw; - } finally { _columnMappings.ReadOnly = false; @@ -2791,40 +2765,17 @@ private Task CopyBatchesAsyncContinuedOnSuccess(BulkCopySimpleResultSet internal // Takes care of cleaning up the parser, stateObj and transaction when CopyBatchesAsync fails. private void CopyBatchesAsyncContinuedOnError(bool cleanupParser) { - SqlInternalConnectionTds internalConnection = _connection.GetOpenTdsConnection(); - - #if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); - #endif - try - { - if ((cleanupParser) && (_parser != null) && (_stateObj != null)) - { - _parser._asyncWrite = false; - Task task = _parser.WriteBulkCopyDone(_stateObj); - Debug.Assert(task == null, "Write should not pend when error occurs"); - RunParser(); - } - - if (_stateObj != null) - { - CleanUpStateObject(); - } - } - catch (OutOfMemoryException) - { - internalConnection.DoomThisConnection(); - throw; - } - catch (StackOverflowException) + if ((cleanupParser) && (_parser != null) && (_stateObj != null)) { - internalConnection.DoomThisConnection(); - throw; + _parser._asyncWrite = false; + Task task = _parser.WriteBulkCopyDone(_stateObj); + Debug.Assert(task == null, "Write should not pend when error occurs"); + RunParser(); } - catch (ThreadAbortException) + + if (_stateObj != null) { - internalConnection.DoomThisConnection(); - throw; + CleanUpStateObject(); } AbortTransaction(); From 224ddf673852c56028dcc370196bcf9ab766ed00 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:12:33 -0500 Subject: [PATCH 02/21] Remove CER from SqlDataReader --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 957 ++++++------------ 1 file changed, 294 insertions(+), 663 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 926b714462..877781d70e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -263,44 +263,11 @@ internal _SqlMetaDataSet MetaData { throw SQL.PendingBeginXXXExists(); } - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); - if (TryConsumeMetaData() != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } - } - catch (System.OutOfMemoryException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.StackOverflowException e) + + Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); + if (TryConsumeMetaData() != TdsOperationStatus.Done) { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; + throw SQL.SynchronousCallMayNotPend(); } } @@ -875,43 +842,10 @@ private TdsOperationStatus TryCleanPartialRead() private void CleanPartialReadReliable() { AssertReaderState(requireData: true, permitAsync: false); - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - TdsOperationStatus result = TryCleanPartialRead(); - Debug.Assert(result == TdsOperationStatus.Done, "Should not pend on sync call"); - Debug.Assert(!_sharedState._dataReady, "_dataReady should be cleared"); - } - catch (System.OutOfMemoryException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.StackOverflowException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } + + TdsOperationStatus result = TryCleanPartialRead(); + Debug.Assert(result == TdsOperationStatus.Done, "Should not pend on sync call"); + Debug.Assert(!_sharedState._dataReady, "_dataReady should be cleared"); } /// @@ -1027,13 +961,9 @@ private TdsOperationStatus TryCloseInternal(bool closeReader) TdsParser parser = _parser; TdsParserStateObject stateObj = _stateObj; bool closeConnection = (IsCommandBehavior(CommandBehavior.CloseConnection)); - bool aborting = false; bool cleanDataFailed = false; TdsOperationStatus result; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { if ((!_isClosed) && (parser != null) && (stateObj != null) && (stateObj.HasPendingData)) @@ -1091,48 +1021,9 @@ private TdsOperationStatus TryCloseInternal(bool closeReader) RestoreServerSettings(parser, stateObj); return TdsOperationStatus.Done; } - catch (System.OutOfMemoryException e) - { - _isClosed = true; - aborting = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.StackOverflowException e) - { - _isClosed = true; - aborting = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _isClosed = true; - aborting = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } finally { - if (aborting) - { - _isClosed = true; - _command = null; // we are done at this point, don't allow navigation to the connection - _connection = null; - _statistics = null; - _stateObj = null; - _parser = null; - } - else if (closeReader) + if (closeReader) { bool wasClosed = _isClosed; _isClosed = true; @@ -1158,55 +1049,25 @@ private TdsOperationStatus TryCloseInternal(bool closeReader) { Connection.RemoveWeakReference(this); // This doesn't catch everything -- the connection may be closed, but it prevents dead readers from clogging the collection } - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try + + // IsClosed may be true if CloseReaderFromConnection was called - in which case, the session has already been closed + if (!wasClosed && stateObj != null) { - // IsClosed may be true if CloseReaderFromConnection was called - in which case, the session has already been closed - if (!wasClosed && stateObj != null) + if (!cleanDataFailed) { - if (!cleanDataFailed) - { - stateObj.CloseSession(); - } - else - { - if (parser != null) - { - parser.State = TdsParserState.Broken; // We failed while draining data, so TDS pointer can be between tokens - cannot recover - parser.PutSession(stateObj); - parser.Connection.BreakConnection(); - } - } + stateObj.CloseSession(); } - // DO NOT USE stateObj after this point - it has been returned to the TdsParser's session pool and potentially handed out to another thread - } - catch (System.OutOfMemoryException e) - { - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.StackOverflowException e) - { - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - if (_connection != null) + else { - _connection.Abort(e); + if (parser != null) + { + parser.State = TdsParserState.Broken; // We failed while draining data, so TDS pointer can be between tokens - cannot recover + parser.PutSession(stateObj); + parser.Connection.BreakConnection(); + } } - throw; } + // DO NOT USE stateObj after this point - it has been returned to the TdsParser's session pool and potentially handed out to another thread // do not retry here @@ -1791,265 +1652,232 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf { remaining = 0; TdsOperationStatus result; + + int cbytes = 0; + AssertReaderState(requireData: true, permitAsync: true, columnIndex: i, enforceSequentialAccess: true); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try + // sequential reading + if (IsCommandBehavior(CommandBehavior.SequentialAccess)) { - int cbytes = 0; - AssertReaderState(requireData: true, permitAsync: true, columnIndex: i, enforceSequentialAccess: true); + Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has an active Stream or TextReader"); - // sequential reading - if (IsCommandBehavior(CommandBehavior.SequentialAccess)) + if (_metaData[i] != null && _metaData[i].cipherMD != null) { - Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has an active Stream or TextReader"); - - if (_metaData[i] != null && _metaData[i].cipherMD != null) - { - throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column); - } - - if (_sharedState._nextColumnHeaderToRead <= i) - { - result = TryReadColumnHeader(i); - if (result != TdsOperationStatus.Done) - { - return result; - } - } - - // If data is null, ReadColumnHeader sets the data.IsNull bit. - if (_data[i] != null && _data[i].IsNull) - { - throw new SqlNullValueException(); - } - - // If there are an unknown (-1) number of bytes left for a PLP, read its size - if ((-1 == _sharedState._columnDataBytesRemaining) && (_metaData[i].metaType.IsPlp)) - { - ulong left; - result = _parser.TryPlpBytesLeft(_stateObj, out left); - if (result != TdsOperationStatus.Done) - { - return result; - } - _sharedState._columnDataBytesRemaining = (long)left; - } - - if (0 == _sharedState._columnDataBytesRemaining) - { - return TdsOperationStatus.Done; // We've read this column to the end - } + throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column); + } - // if no buffer is passed in, return the number total of bytes, or -1 - if (buffer == null) + if (_sharedState._nextColumnHeaderToRead <= i) + { + result = TryReadColumnHeader(i); + if (result != TdsOperationStatus.Done) { - if (_metaData[i].metaType.IsPlp) - { - remaining = (long)_parser.PlpBytesTotalLength(_stateObj); - return TdsOperationStatus.Done; - } - remaining = _sharedState._columnDataBytesRemaining; - return TdsOperationStatus.Done; + return result; } + } - if (dataIndex < 0) - { - throw ADP.NegativeParameter(nameof(dataIndex)); - } + // If data is null, ReadColumnHeader sets the data.IsNull bit. + if (_data[i] != null && _data[i].IsNull) + { + throw new SqlNullValueException(); + } - if (dataIndex < _columnDataBytesRead) + // If there are an unknown (-1) number of bytes left for a PLP, read its size + if ((-1 == _sharedState._columnDataBytesRemaining) && (_metaData[i].metaType.IsPlp)) + { + ulong left; + result = _parser.TryPlpBytesLeft(_stateObj, out left); + if (result != TdsOperationStatus.Done) { - throw ADP.NonSeqByteAccess(dataIndex, _columnDataBytesRead, nameof(GetBytes)); + return result; } + _sharedState._columnDataBytesRemaining = (long)left; + } - // if the dataIndex is not equal to bytes read, then we have to skip bytes - long cb = dataIndex - _columnDataBytesRead; + if (0 == _sharedState._columnDataBytesRemaining) + { + return TdsOperationStatus.Done; // We've read this column to the end + } - // if dataIndex is outside of the data range, return 0 - if ((cb > _sharedState._columnDataBytesRemaining) && !_metaData[i].metaType.IsPlp) + // if no buffer is passed in, return the number total of bytes, or -1 + if (buffer == null) + { + if (_metaData[i].metaType.IsPlp) { + remaining = (long)_parser.PlpBytesTotalLength(_stateObj); return TdsOperationStatus.Done; } - - // if bad buffer index, throw - if (bufferIndex < 0 || bufferIndex >= buffer.Length) - { - throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, nameof(bufferIndex)); - } - - // if there is not enough room in the buffer for data - if (length + bufferIndex > buffer.Length) - { - throw ADP.InvalidBufferSizeOrIndex(length, bufferIndex); - } - - if (length < 0) - { - throw ADP.InvalidDataLength(length); - } - - // Skip if needed - if (cb > 0) - { - if (_metaData[i].metaType.IsPlp) - { - ulong skipped; - result = _parser.TrySkipPlpValue((ulong)cb, _stateObj, out skipped); - if (result != TdsOperationStatus.Done) - { - return result; - } - _columnDataBytesRead += (long)skipped; - } - else - { - result = _stateObj.TrySkipLongBytes(cb); - if (result != TdsOperationStatus.Done) - { - return result; - } - _columnDataBytesRead += cb; - _sharedState._columnDataBytesRemaining -= cb; - } - } - - int bytesRead; - result = TryGetBytesInternalSequential(i, buffer, bufferIndex, length, out bytesRead); - remaining = (int)bytesRead; - return result; + remaining = _sharedState._columnDataBytesRemaining; + return TdsOperationStatus.Done; } - // random access now! - // note that since we are caching in an array, and arrays aren't 64 bit ready yet, - // we need can cast to int if the dataIndex is in range if (dataIndex < 0) { throw ADP.NegativeParameter(nameof(dataIndex)); } - if (dataIndex > int.MaxValue) + if (dataIndex < _columnDataBytesRead) { - throw ADP.InvalidSourceBufferIndex(cbytes, dataIndex, nameof(dataIndex)); + throw ADP.NonSeqByteAccess(dataIndex, _columnDataBytesRead, nameof(GetBytes)); } - int ndataIndex = (int)dataIndex; - byte[] data; + // if the dataIndex is not equal to bytes read, then we have to skip bytes + long cb = dataIndex - _columnDataBytesRead; - // WebData 99342 - in the non-sequential case, we need to support - // the use of GetBytes on string data columns, but - // GetSqlBinary isn't supposed to. What we end up - // doing isn't exactly pretty, but it does work. - if (_metaData[i].metaType.IsBinType) + // if dataIndex is outside of the data range, return 0 + if ((cb > _sharedState._columnDataBytesRemaining) && !_metaData[i].metaType.IsPlp) { - data = GetSqlBinary(i).Value; + return TdsOperationStatus.Done; } - else - { - Debug.Assert(_metaData[i].metaType.IsLong, "non long type?"); - Debug.Assert(_metaData[i].metaType.IsCharType, "non-char type?"); - SqlString temp = GetSqlString(i); - if (_metaData[i].metaType.IsNCharType) - { - data = temp.GetUnicodeBytes(); - } - else - { - data = temp.GetNonUnicodeBytes(); - } + // if bad buffer index, throw + if (bufferIndex < 0 || bufferIndex >= buffer.Length) + { + throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, nameof(bufferIndex)); } - cbytes = data.Length; - - // if no buffer is passed in, return the number of characters we have - if (buffer == null) + // if there is not enough room in the buffer for data + if (length + bufferIndex > buffer.Length) { - remaining = cbytes; - return TdsOperationStatus.Done; + throw ADP.InvalidBufferSizeOrIndex(length, bufferIndex); } - // if dataIndex is outside of data range, return 0 - if (ndataIndex < 0 || ndataIndex >= cbytes) + if (length < 0) { - return TdsOperationStatus.Done; + throw ADP.InvalidDataLength(length); } - try + + // Skip if needed + if (cb > 0) { - if (ndataIndex < cbytes) + if (_metaData[i].metaType.IsPlp) { - // help the user out in the case where there's less data than requested - if ((ndataIndex + length) > cbytes) + ulong skipped; + result = _parser.TrySkipPlpValue((ulong)cb, _stateObj, out skipped); + if (result != TdsOperationStatus.Done) { - cbytes = cbytes - ndataIndex; + return result; } - else + _columnDataBytesRead += (long)skipped; + } + else + { + result = _stateObj.TrySkipLongBytes(cb); + if (result != TdsOperationStatus.Done) { - cbytes = length; + return result; } + _columnDataBytesRead += cb; + _sharedState._columnDataBytesRemaining -= cb; } + } + + int bytesRead; + result = TryGetBytesInternalSequential(i, buffer, bufferIndex, length, out bytesRead); + remaining = (int)bytesRead; + return result; + } + + // random access now! + // note that since we are caching in an array, and arrays aren't 64 bit ready yet, + // we need can cast to int if the dataIndex is in range + if (dataIndex < 0) + { + throw ADP.NegativeParameter(nameof(dataIndex)); + } + + if (dataIndex > int.MaxValue) + { + throw ADP.InvalidSourceBufferIndex(cbytes, dataIndex, nameof(dataIndex)); + } + + int ndataIndex = (int)dataIndex; + byte[] data; + + // WebData 99342 - in the non-sequential case, we need to support + // the use of GetBytes on string data columns, but + // GetSqlBinary isn't supposed to. What we end up + // doing isn't exactly pretty, but it does work. + if (_metaData[i].metaType.IsBinType) + { + data = GetSqlBinary(i).Value; + } + else + { + Debug.Assert(_metaData[i].metaType.IsLong, "non long type?"); + Debug.Assert(_metaData[i].metaType.IsCharType, "non-char type?"); - Buffer.BlockCopy(data, ndataIndex, buffer, bufferIndex, cbytes); + SqlString temp = GetSqlString(i); + if (_metaData[i].metaType.IsNCharType) + { + data = temp.GetUnicodeBytes(); } - catch (Exception e) + else { - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - cbytes = data.Length; + data = temp.GetNonUnicodeBytes(); + } + } - if (length < 0) - { - throw ADP.InvalidDataLength(length); - } + cbytes = data.Length; - // if bad buffer index, throw - if (bufferIndex < 0 || bufferIndex >= buffer.Length) + // if no buffer is passed in, return the number of characters we have + if (buffer == null) + { + remaining = cbytes; + return TdsOperationStatus.Done; + } + + // if dataIndex is outside of data range, return 0 + if (ndataIndex < 0 || ndataIndex >= cbytes) + { + return TdsOperationStatus.Done; + } + try + { + if (ndataIndex < cbytes) + { + // help the user out in the case where there's less data than requested + if ((ndataIndex + length) > cbytes) { - throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, nameof(bufferIndex)); + cbytes = cbytes - ndataIndex; } - - // if there is not enough room in the buffer for data - if (cbytes + bufferIndex > buffer.Length) + else { - throw ADP.InvalidBufferSizeOrIndex(cbytes, bufferIndex); + cbytes = length; } - - throw; } - remaining = cbytes; - return TdsOperationStatus.Done; + Buffer.BlockCopy(data, ndataIndex, buffer, bufferIndex, cbytes); } - catch (System.OutOfMemoryException e) + catch (Exception e) { - _isClosed = true; - if (_connection != null) + if (!ADP.IsCatchableExceptionType(e)) { - _connection.Abort(e); + throw; } - throw; - } - catch (System.StackOverflowException e) - { - _isClosed = true; - if (_connection != null) + cbytes = data.Length; + + if (length < 0) { - _connection.Abort(e); + throw ADP.InvalidDataLength(length); } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _isClosed = true; - if (_connection != null) + + // if bad buffer index, throw + if (bufferIndex < 0 || bufferIndex >= buffer.Length) + { + throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, nameof(bufferIndex)); + } + + // if there is not enough room in the buffer for data + if (cbytes + bufferIndex > buffer.Length) { - _connection.Abort(e); + throw ADP.InvalidBufferSizeOrIndex(cbytes, bufferIndex); } + throw; } + + remaining = cbytes; + return TdsOperationStatus.Done; } internal int GetBytesInternalSequential(int i, byte[] buffer, int index, int length, long? timeoutMilliseconds = null) @@ -2102,78 +1930,45 @@ internal TdsOperationStatus TryGetBytesInternalSequential(int i, byte[] buffer, bytesRead = 0; TdsOperationStatus result; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try + if ((_sharedState._columnDataBytesRemaining == 0) || (length == 0)) { - if ((_sharedState._columnDataBytesRemaining == 0) || (length == 0)) - { - // No data left or nothing requested, return 0 - bytesRead = 0; - return TdsOperationStatus.Done; - } - else + // No data left or nothing requested, return 0 + bytesRead = 0; + return TdsOperationStatus.Done; + } + else + { + // if plp columns, do partial reads. Don't read the entire value in one shot. + if (_metaData[i].metaType.IsPlp) { - // if plp columns, do partial reads. Don't read the entire value in one shot. - if (_metaData[i].metaType.IsPlp) + // Read in data + result = _stateObj.TryReadPlpBytes(ref buffer, index, length, out bytesRead); + _columnDataBytesRead += bytesRead; + if (result != TdsOperationStatus.Done) { - // Read in data - result = _stateObj.TryReadPlpBytes(ref buffer, index, length, out bytesRead); - _columnDataBytesRead += bytesRead; - if (result != TdsOperationStatus.Done) - { - return result; - } - - // Query for number of bytes left - ulong left; - result = _parser.TryPlpBytesLeft(_stateObj, out left); - if (result != TdsOperationStatus.Done) - { - _sharedState._columnDataBytesRemaining = -1; - return result; - } - _sharedState._columnDataBytesRemaining = (long)left; - return TdsOperationStatus.Done; + return result; } - else + + // Query for number of bytes left + ulong left; + result = _parser.TryPlpBytesLeft(_stateObj, out left); + if (result != TdsOperationStatus.Done) { - // Read data (not exceeding the total amount of data available) - int bytesToRead = (int)Math.Min((long)length, _sharedState._columnDataBytesRemaining); - result = _stateObj.TryReadByteArray(buffer.AsSpan(index), bytesToRead, out bytesRead); - _columnDataBytesRead += bytesRead; - _sharedState._columnDataBytesRemaining -= bytesRead; + _sharedState._columnDataBytesRemaining = -1; return result; } + _sharedState._columnDataBytesRemaining = (long)left; + return TdsOperationStatus.Done; } - } - catch (System.OutOfMemoryException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.StackOverflowException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _isClosed = true; - if (_connection != null) + else { - _connection.Abort(e); + // Read data (not exceeding the total amount of data available) + int bytesToRead = (int)Math.Min((long)length, _sharedState._columnDataBytesRemaining); + result = _stateObj.TryReadByteArray(buffer.AsSpan(index), bytesToRead, out bytesRead); + _columnDataBytesRead += bytesRead; + _sharedState._columnDataBytesRemaining -= bytesRead; + return result; } - throw; } } @@ -2446,126 +2241,93 @@ override public long GetChars(int i, long dataIndex, char[] buffer, int bufferIn private long GetCharsFromPlpData(int i, long dataIndex, char[] buffer, int bufferIndex, int length) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - long cch; - - AssertReaderState(requireData: true, permitAsync: false, columnIndex: i, enforceSequentialAccess: true); - Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has active Stream or TextReader"); - // don't allow get bytes on non-long or non-binary columns - Debug.Assert(_metaData[i].metaType.IsPlp, "GetCharsFromPlpData called on a non-plp column!"); - // Must be sequential reading - Debug.Assert(IsCommandBehavior(CommandBehavior.SequentialAccess), "GetCharsFromPlpData called for non-Sequential access"); - - if (!_metaData[i].metaType.IsCharType) - { - throw SQL.NonCharColumn(_metaData[i].column); - } + long cch; - if (_sharedState._nextColumnHeaderToRead <= i) - { - ReadColumnHeader(i); - } + AssertReaderState(requireData: true, permitAsync: false, columnIndex: i, enforceSequentialAccess: true); + Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has active Stream or TextReader"); + // don't allow get bytes on non-long or non-binary columns + Debug.Assert(_metaData[i].metaType.IsPlp, "GetCharsFromPlpData called on a non-plp column!"); + // Must be sequential reading + Debug.Assert(IsCommandBehavior(CommandBehavior.SequentialAccess), "GetCharsFromPlpData called for non-Sequential access"); - // If data is null, ReadColumnHeader sets the data.IsNull bit. - if (_data[i] != null && _data[i].IsNull) - { - throw new SqlNullValueException(); - } + if (!_metaData[i].metaType.IsCharType) + { + throw SQL.NonCharColumn(_metaData[i].column); + } - if (dataIndex < _columnDataCharsRead) - { - // Don't allow re-read of same chars in sequential access mode - throw ADP.NonSeqByteAccess(dataIndex, _columnDataCharsRead, nameof(GetChars)); - } + if (_sharedState._nextColumnHeaderToRead <= i) + { + ReadColumnHeader(i); + } - // If we start reading the new column, either dataIndex is 0 or - // _columnDataCharsRead is 0 and dataIndex > _columnDataCharsRead is true below. - // In both cases we will clean decoder - if (dataIndex == 0) - { - _stateObj._plpdecoder = null; - } + // If data is null, ReadColumnHeader sets the data.IsNull bit. + if (_data[i] != null && _data[i].IsNull) + { + throw new SqlNullValueException(); + } - bool isUnicode = _metaData[i].metaType.IsNCharType; + if (dataIndex < _columnDataCharsRead) + { + // Don't allow re-read of same chars in sequential access mode + throw ADP.NonSeqByteAccess(dataIndex, _columnDataCharsRead, nameof(GetChars)); + } - // If there are an unknown (-1) number of bytes left for a PLP, read its size - if (-1 == _sharedState._columnDataBytesRemaining) - { - _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj); - } + // If we start reading the new column, either dataIndex is 0 or + // _columnDataCharsRead is 0 and dataIndex > _columnDataCharsRead is true below. + // In both cases we will clean decoder + if (dataIndex == 0) + { + _stateObj._plpdecoder = null; + } - if (0 == _sharedState._columnDataBytesRemaining) - { - _stateObj._plpdecoder = null; - return 0; // We've read this column to the end - } + bool isUnicode = _metaData[i].metaType.IsNCharType; - // if no buffer is passed in, return the total number of characters or -1 - if (buffer == null) - { - cch = (long)_parser.PlpBytesTotalLength(_stateObj); - return (isUnicode && (cch > 0)) ? cch >> 1 : cch; - } - if (dataIndex > _columnDataCharsRead) - { - // Skip chars + // If there are an unknown (-1) number of bytes left for a PLP, read its size + if (-1 == _sharedState._columnDataBytesRemaining) + { + _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj); + } - // Clean decoder state: we do not reset it, but destroy to ensure - // that we do not start decoding the column with decoder from the old one - _stateObj._plpdecoder = null; - cch = dataIndex - _columnDataCharsRead; - cch = isUnicode ? (cch << 1) : cch; - cch = (long)_parser.SkipPlpValue((ulong)(cch), _stateObj); - _columnDataBytesRead += cch; - _columnDataCharsRead += (isUnicode && (cch > 0)) ? cch >> 1 : cch; - } - cch = length; + if (0 == _sharedState._columnDataBytesRemaining) + { + _stateObj._plpdecoder = null; + return 0; // We've read this column to the end + } - if (isUnicode) - { - cch = (long)_parser.ReadPlpUnicodeChars(ref buffer, bufferIndex, length, _stateObj); - _columnDataBytesRead += (cch << 1); - } - else - { - cch = (long)_parser.ReadPlpAnsiChars(ref buffer, bufferIndex, length, _metaData[i], _stateObj); - _columnDataBytesRead += cch << 1; - } - _columnDataCharsRead += cch; - _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj); - return cch; + // if no buffer is passed in, return the total number of characters or -1 + if (buffer == null) + { + cch = (long)_parser.PlpBytesTotalLength(_stateObj); + return (isUnicode && (cch > 0)) ? cch >> 1 : cch; } - catch (System.OutOfMemoryException e) + if (dataIndex > _columnDataCharsRead) { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; + // Skip chars + + // Clean decoder state: we do not reset it, but destroy to ensure + // that we do not start decoding the column with decoder from the old one + _stateObj._plpdecoder = null; + cch = dataIndex - _columnDataCharsRead; + cch = isUnicode ? (cch << 1) : cch; + cch = (long)_parser.SkipPlpValue((ulong)(cch), _stateObj); + _columnDataBytesRead += cch; + _columnDataCharsRead += (isUnicode && (cch > 0)) ? cch >> 1 : cch; } - catch (System.StackOverflowException e) + cch = length; + + if (isUnicode) { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; + cch = (long)_parser.ReadPlpUnicodeChars(ref buffer, bufferIndex, length, _stateObj); + _columnDataBytesRead += (cch << 1); } - catch (System.Threading.ThreadAbortException e) + else { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; + cch = (long)_parser.ReadPlpAnsiChars(ref buffer, bufferIndex, length, _metaData[i], _stateObj); + _columnDataBytesRead += cch << 1; } + _columnDataCharsRead += cch; + _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj); + return cch; } internal long GetStreamingXmlChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) @@ -3724,10 +3486,6 @@ private TdsOperationStatus TryNextResult(out bool more) SqlStatistics statistics = null; using (TryEventScope.Create("SqlDataReader.NextResult | API | Object Id {0}", ObjectID)) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try { statistics = SqlStatistics.StartTimer(Statistics); @@ -3863,33 +3621,6 @@ private TdsOperationStatus TryNextResult(out bool more) more = success; return TdsOperationStatus.Done; } - catch (System.OutOfMemoryException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.StackOverflowException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } finally { SqlStatistics.StopTimer(statistics); @@ -3924,10 +3655,6 @@ private TdsOperationStatus TryReadInternal(bool setTimeout, out bool more) SqlStatistics statistics = null; using (TryEventScope.Create("SqlDataReader.TryReadInternal | API | Object Id {0}", ObjectID)) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try { TdsOperationStatus result; @@ -4087,36 +3814,6 @@ private TdsOperationStatus TryReadInternal(bool setTimeout, out bool more) return TdsOperationStatus.Done; } - catch (OutOfMemoryException e) - { - _isClosed = true; - SqlConnection con = _connection; - if (con != null) - { - con.Abort(e); - } - throw; - } - catch (StackOverflowException e) - { - _isClosed = true; - SqlConnection con = _connection; - if (con != null) - { - con.Abort(e); - } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _isClosed = true; - SqlConnection con = _connection; - if (con != null) - { - con.Abort(e); - } - throw; - } finally { SqlStatistics.StopTimer(statistics); @@ -4143,55 +3840,22 @@ private TdsOperationStatus TryReadColumn(int i, bool setTimeout, bool allowParti { CheckDataIsReady(columnIndex: i, permitAsync: true, allowPartiallyReadColumn: allowPartiallyReadColumn, methodName: null); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - Debug.Assert(_sharedState._nextColumnHeaderToRead <= _metaData.Length, "_sharedState._nextColumnHeaderToRead too large"); - Debug.Assert(_sharedState._nextColumnDataToRead <= _metaData.Length, "_sharedState._nextColumnDataToRead too large"); - - if (setTimeout) - { - SetTimeout(_defaultTimeoutMilliseconds); - } - - TdsOperationStatus result = TryReadColumnInternal(i, readHeaderOnly: false, forStreaming: forStreaming); - if (result != TdsOperationStatus.Done) - { - return result; - } + Debug.Assert(_sharedState._nextColumnHeaderToRead <= _metaData.Length, "_sharedState._nextColumnHeaderToRead too large"); + Debug.Assert(_sharedState._nextColumnDataToRead <= _metaData.Length, "_sharedState._nextColumnDataToRead too large"); - Debug.Assert(_data[i] != null, " data buffer is null?"); - } - catch (System.OutOfMemoryException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.StackOverflowException e) + if (setTimeout) { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; + SetTimeout(_defaultTimeoutMilliseconds); } - catch (System.Threading.ThreadAbortException e) + + TdsOperationStatus result = TryReadColumnInternal(i, readHeaderOnly: false, forStreaming: forStreaming); + if (result != TdsOperationStatus.Done) { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; + return result; } + Debug.Assert(_data[i] != null, " data buffer is null?"); + return TdsOperationStatus.Done; } @@ -4233,41 +3897,8 @@ private TdsOperationStatus TryReadColumnHeader(int i) { throw SQL.InvalidRead(); } - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - return TryReadColumnInternal(i, readHeaderOnly: true); - } - catch (System.OutOfMemoryException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.StackOverflowException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _isClosed = true; - if (_connection != null) - { - _connection.Abort(e); - } - throw; - } + + return TryReadColumnInternal(i, readHeaderOnly: true); } internal TdsOperationStatus TryReadColumnInternal(int i, bool readHeaderOnly = false, bool forStreaming = false) From 7e1ff6df07fcc5eaea0deee0b01a9513e9aff27e Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:14:56 -0500 Subject: [PATCH 03/21] Remove CER from SqlDependency? --- .../src/Microsoft/Data/SqlClient/SqlDependency.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs index fa787d76f8..b35a78f33e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs @@ -611,9 +611,7 @@ internal static bool Start(string connectionString, string queue, bool useDefaul string database = null; string service = null; bool appDomainStart = false; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { // CER to ensure that if Start succeeds we add to hash completing setup. // Start using process wide default service/queue & database from connection string. @@ -749,9 +747,7 @@ internal static bool Stop(string connectionString, string queue, bool useDefault if (useDefaults) { bool appDomainStop = false; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { // CER to ensure that if Stop succeeds we remove from hash completing teardown. // Start using process wide default service/queue & database from connection string. From e997f8d01f6f5014b9f249033c2446604b73e6ed Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:18:11 -0500 Subject: [PATCH 04/21] Remove CER From WaitHandleDbConnectionPool --- .../WaitHandleDbConnectionPool.cs | 98 +++---------------- 1 file changed, 15 insertions(+), 83 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs index bd6ebf708e..6568fda7f3 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs @@ -811,22 +811,15 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio Timer t = new Timer(new TimerCallback(this.ErrorCallback), null, Timeout.Infinite, Timeout.Infinite); bool timerIsNotDisposed; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { } - finally - { - _waitHandles.ErrorEvent.Set(); - _errorOccurred = true; - - // Enable the timer. - // Note that the timer is created to allow periodic invocation. If ThreadAbort occurs in the middle of ErrorCallback, - // the timer will restart. Otherwise, the timer callback (ErrorCallback) destroys the timer after resetting the error to avoid second callback. - _errorTimer = t; - timerIsNotDisposed = t.Change(_errorWait, _errorWait); - } + + _waitHandles.ErrorEvent.Set(); + _errorOccurred = true; + + // Enable the timer. + // Note that the timer is created to allow periodic invocation. If ThreadAbort occurs in the middle of ErrorCallback, + // the timer will restart. Otherwise, the timer callback (ErrorCallback) destroys the timer after resetting the error to avoid second callback. + _errorTimer = t; + timerIsNotDisposed = t.Change(_errorWait, _errorWait); Debug.Assert(timerIsNotDisposed, "ErrorCallback timer has been disposed"); @@ -1040,22 +1033,10 @@ private void WaitForPendingOpen() do { bool started = false; - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { } - finally - { - started = Interlocked.CompareExchange(ref _pendingOpensWaiting, 1, 0) == 0; - } - + started = Interlocked.CompareExchange(ref _pendingOpensWaiting, 1, 0) == 0; if (!started) { return; @@ -1081,10 +1062,7 @@ private void WaitForPendingOpen() DbConnectionInternal connection = null; bool timeout = false; Exception caughtException = null; - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { ADP.SetCurrentTransaction(next.Completion.Task.AsyncState as Transaction); @@ -1096,24 +1074,6 @@ private void WaitForPendingOpen() next.UserOptions, out connection); } - catch (System.OutOfMemoryException) - { - if (connection != null) - { connection.DoomThisConnection(); } - throw; - } - catch (System.StackOverflowException) - { - if (connection != null) - { connection.DoomThisConnection(); } - throw; - } - catch (System.Threading.ThreadAbortException) - { - if (connection != null) - { connection.DoomThisConnection(); } - throw; - } catch (Exception e) { caughtException = e; @@ -1228,23 +1188,9 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj do { int waitResult = BOGUS_HANDLE; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { -#if NETFRAMEWORK - // We absolutely must have the value of waitResult set, - // or we may leak the mutex in async abort cases. - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - } - finally - { - waitResult = WaitHandle.WaitAny(_waitHandles.GetHandles(allowCreate), unchecked((int)waitForMultipleObjectsTimeout)); - } + waitResult = WaitHandle.WaitAny(_waitHandles.GetHandles(allowCreate), unchecked((int)waitForMultipleObjectsTimeout)); // From the WaitAny docs: "If more than one object became signaled during // the call, this is the array index of the signaled object with the @@ -1328,9 +1274,6 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj { if (_waitHandles.CreationSemaphore.WaitOne(unchecked((int)waitForMultipleObjectsTimeout))) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Creating new connection.", Id); @@ -1559,23 +1502,12 @@ private void PoolCreateRequest(object state) return; } int waitResult = BOGUS_HANDLE; - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { // Obtain creation mutex so we're the only one creating objects // and we must have the wait result -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { } - finally - { - waitResult = WaitHandle.WaitAny(_waitHandles.GetHandles(withCreate: true), CreationTimeout); - } + waitResult = WaitHandle.WaitAny(_waitHandles.GetHandles(withCreate: true), CreationTimeout); if (CREATION_HANDLE == waitResult) { DbConnectionInternal newObj; From 849d49a2bf66971307e1cea3c63349368ae83de7 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:27:29 -0500 Subject: [PATCH 05/21] Remove CER from SqlTransaction --- .../Data/SqlClient/SqlTransaction.cs | 149 +----------------- 1 file changed, 6 insertions(+), 143 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 8130b529d7..58a371ec7f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -106,41 +106,15 @@ public override void Commit() ObjectId, ActivityCorrelator.Current, Connection?.ClientConnectionId); - - #if NETFRAMEWORK - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - #endif + try { - #if NETFRAMEWORK - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - #endif - statistics = SqlStatistics.StartTimer(Statistics); _isFromApi = true; InternalTransaction.Commit(); } - #if NETFRAMEWORK - catch (OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (ThreadAbortException e) - { - _connection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } - #endif catch (SqlException ex) { #if NET @@ -176,40 +150,9 @@ protected override void Dispose(bool disposing) { if (disposing) { - #if NETFRAMEWORK - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - #endif - try + if (!IsZombied && !Is2005PartialZombie) { - #if NETFRAMEWORK - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - #endif - - if (!IsZombied && !Is2005PartialZombie) - { - InternalTransaction.Dispose(); - } - } - catch (OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (ThreadAbortException e) - { - _connection.Abort(e); - - #if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - #endif - - throw; + InternalTransaction.Dispose(); } } @@ -248,40 +191,16 @@ public override void Rollback() ObjectId, ActivityCorrelator.Current, Connection?.ClientConnectionId); - - #if NETFRAMEWORK - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - #endif + try { - #if NETFRAMEWORK - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - #endif statistics = SqlStatistics.StartTimer(Statistics); _isFromApi = true; InternalTransaction.Rollback(); } - #if NETFRAMEWORK - catch (OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (ThreadAbortException e) - { - _connection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } - #else + #if NET catch (Exception ex) { diagnosticScope.SetException(ex); @@ -327,40 +246,14 @@ public void Rollback(string transactionName) using (eventScopeEnter) { SqlStatistics statistics = null; - - #if NETFRAMEWORK - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - #endif try { - #if NETFRAMEWORK - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - #endif - statistics = SqlStatistics.StartTimer(Statistics); _isFromApi = true; InternalTransaction.Rollback(transactionName); } - #if NETFRAMEWORK - catch (OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (ThreadAbortException e) - { - _connection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } - #else + #if NET catch (Exception ex) { diagnosticScope.SetException(ex); @@ -391,42 +284,12 @@ public void Save(string savePointName) SqlStatistics statistics = null; using (TryEventScope.Create("SqlTransaction.Save | API | Object Id {0} | Save Point Name '{1}'", ObjectId, savePointName)) { - #if NETFRAMEWORK - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - #endif try { - #if NETFRAMEWORK - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - #endif - statistics = SqlStatistics.StartTimer(Statistics); InternalTransaction.Save(savePointName); } - #if NETFRAMEWORK - catch (OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (ThreadAbortException e) - { - _connection.Abort(e); - - #if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - #endif - - throw; - } - #endif finally { SqlStatistics.StopTimer(statistics); From 453a1bcd2b47ef1fb95123805d9fab4241a974d8 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:30:45 -0500 Subject: [PATCH 06/21] Remove CER from SqlUtil --- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 82 ------------------- 1 file changed, 82 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs index 94df331f22..454505961c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -180,50 +180,10 @@ internal static void ContinueTask(Task task, #if NETFRAMEWORK if (connectionToDoom != null || connectionToAbort != null) { - RuntimeHelpers.PrepareConstrainedRegions(); try { onSuccess(); } - catch (System.OutOfMemoryException e) - { - if (connectionToDoom != null) - { - connectionToDoom.DoomThisConnection(); - } - else - { - connectionToAbort.Abort(e); - } - completion.SetException(e); - throw; - } - catch (System.StackOverflowException e) - { - if (connectionToDoom != null) - { - connectionToDoom.DoomThisConnection(); - } - else - { - connectionToAbort.Abort(e); - } - completion.SetException(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - if (connectionToDoom != null) - { - connectionToDoom.DoomThisConnection(); - } - else - { - connectionToAbort.Abort(e); - } - completion.SetException(e); - throw; - } catch (Exception e) { completion.SetException(e); @@ -312,52 +272,10 @@ internal static void ContinueTaskWithState(Task task, } else if (connectionToDoom != null || connectionToAbort != null) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { onSuccess(state2); } - catch (System.OutOfMemoryException e) - { - if (connectionToDoom != null) - { - connectionToDoom.DoomThisConnection(); - } - else - { - connectionToAbort.Abort(e); - } - completion.SetException(e); - throw; - } - catch (System.StackOverflowException e) - { - if (connectionToDoom != null) - { - connectionToDoom.DoomThisConnection(); - } - else - { - connectionToAbort.Abort(e); - } - completion.SetException(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - if (connectionToDoom != null) - { - connectionToDoom.DoomThisConnection(); - } - else - { - connectionToAbort.Abort(e); - } - completion.SetException(e); - throw; - } catch (Exception e) { completion.SetException(e); From 236922636d48acb0ebd441e23c507c25d628cc6d Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:34:12 -0500 Subject: [PATCH 07/21] Remove CER from SniNativeWrapper --- .../src/Interop/Windows/Sni/SniNativeWrapper.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs index c9cbef64a4..d0d4dfb125 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs @@ -332,10 +332,7 @@ internal static void SniPacketSetData( bool mustRelease = false; bool mustClearBuffer = false; IntPtr clearPassword = IntPtr.Zero; - - // provides a guaranteed finally block – without this it isn’t guaranteed – non- - // interruptible by fatal exceptions - RuntimeHelpers.PrepareConstrainedRegions(); + try { unsafe @@ -348,9 +345,6 @@ internal static void SniPacketSetData( // SecureString is used if (passwords[i] != null) { - // provides a guaranteed finally block – without this it isn’t - // guaranteed – non-interruptible by fatal exceptions - RuntimeHelpers.PrepareConstrainedRegions(); try { // ============================================================ From 0a943ad9866b60be2b8be45fc381dd40f5b0aa2c Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:35:06 -0500 Subject: [PATCH 08/21] Remove CER from netcore SqlCommand --- .../netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index f4f38dbe73..2b009e38f7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -4114,9 +4114,7 @@ private SqlDataReader GetParameterEncryptionDataReader(out Task returnTask, Task SqlCommand command = (SqlCommand)state; bool processFinallyBlockAsync = true; bool decrementAsyncCountInFinallyBlockAsync = true; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { // Check for any exceptions on network write, before reading. @@ -4187,10 +4185,7 @@ private SqlDataReader GetParameterEncryptionDataReaderAsync(out Task returnTask, { bool processFinallyBlockAsync = true; bool decrementAsyncCountInFinallyBlockAsync = true; - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { // Check for any exceptions on network write, before reading. From 1d2ac0578e76d107b1eaff3a1c0928a95a071fbd Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:37:56 -0500 Subject: [PATCH 09/21] Remove CER from SqlConnection (netcore) and SqlCommandBuilder --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 53 +------------------ .../Data/SqlClient/SqlCommandBuilder.cs | 28 +--------- 2 files changed, 3 insertions(+), 78 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index d4f13efd80..372d0e1ab0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1237,35 +1237,12 @@ public override void ChangeDatabase(string database) SqlStatistics statistics = null; RepairInnerConnection(); SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.ChangeDatabase | API | Correlation | Object Id {0}, Activity Id {1}, Database {2}", ObjectID, ActivityCorrelator.Current, database); - TdsParser bestEffortCleanupTarget = null; - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this); statistics = SqlStatistics.StartTimer(Statistics); InnerConnection.ChangeDatabase(database); } - catch (System.OutOfMemoryException e) - { - Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - Abort(e); -#if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } finally { SqlStatistics.StopTimer(statistics); @@ -1328,15 +1305,10 @@ public override void Close() } SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; Exception e = null; - -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif + try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this); statistics = SqlStatistics.StartTimer(Statistics); Task reconnectTask = _currentReconnectionTask; @@ -1362,27 +1334,6 @@ public override void Close() _statistics._closeTimestamp = ADP.TimerCurrent(); } } - catch (System.OutOfMemoryException ex) - { - e = ex; - Abort(ex); - throw; - } - catch (System.StackOverflowException ex) - { - e = ex; - Abort(ex); - throw; - } - catch (System.Threading.ThreadAbortException ex) - { - e = ex; - Abort(ex); -#if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } catch (Exception ex) { e = ex; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs index 51e71cc14b..88532e8321 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs @@ -254,34 +254,8 @@ public static void DeriveParameters(SqlCommand command) { throw ADP.ArgumentNull(nameof(command)); } - TdsParser bestEffortCleanupTarget = null; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(command.Connection); - command.DeriveParameters(); - } - catch (OutOfMemoryException e) - { - command?.Connection?.Abort(e); - throw; - } - catch (StackOverflowException e) - { - command?.Connection?.Abort(e); - throw; - } - catch (ThreadAbortException e) - { - command?.Connection?.Abort(e); -#if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } + command.DeriveParameters(); } /// From c4cfb0be234823102509cbe454f1177f4cf2d0cf Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:41:25 -0500 Subject: [PATCH 10/21] Remove CER from SqlDelegatedTransaction --- .../Data/SqlClient/SqlDelegatedTransaction.cs | 389 +++++++----------- 1 file changed, 154 insertions(+), 235 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index 02c10569bc..a3b8fde8a1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -81,47 +81,27 @@ public void Initialize() SqlInternalConnection connection = _connection; SqlConnection usersConnection = connection.Connection; SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Initialize | RES | CPOOL | Object Id {0}, Client Connection Id {1}, delegating transaction.", ObjectID, usersConnection?.ClientConnectionId); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - if (connection.IsEnlistedInTransaction) - { - SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Initialize | RES | CPOOL | {0}, Client Connection Id {1}, was enlisted, now defecting.", ObjectID, usersConnection?.ClientConnectionId); - // defect first - connection.EnlistNull(); - } + if (connection.IsEnlistedInTransaction) + { + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Initialize | RES | CPOOL | {0}, Client Connection Id {1}, was enlisted, now defecting.", ObjectID, usersConnection?.ClientConnectionId); - _internalTransaction = new SqlInternalTransaction(connection, TransactionType.Delegated, null); + // defect first + connection.EnlistNull(); + } - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Begin, null, _isolationLevel, _internalTransaction, true); + _internalTransaction = new SqlInternalTransaction(connection, TransactionType.Delegated, null); - // Handle case where ExecuteTran didn't produce a new transaction, but also didn't throw. - if (connection.CurrentTransaction == null) - { - connection.DoomThisConnection(); - throw ADP.InternalError(ADP.InternalErrorCode.UnknownTransactionFailure); - } + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Begin, null, _isolationLevel, _internalTransaction, true); - _active = true; - } - catch (System.OutOfMemoryException e) + // Handle case where ExecuteTran didn't produce a new transaction, but also didn't throw. + if (connection.CurrentTransaction == null) { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - usersConnection.Abort(e); - throw; + connection.DoomThisConnection(); + throw ADP.InternalError(ADP.InternalErrorCode.UnknownTransactionFailure); } + + _active = true; } internal bool IsActive @@ -146,72 +126,52 @@ public byte[] Promote() { SqlConnection usersConnection = connection.Connection; SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Promote | RES | CPOOL | Object Id {0}, Client Connection Id {1}, promoting transaction.", ObjectID, usersConnection?.ClientConnectionId); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try + + lock (connection) { - lock (connection) + try { - try - { - // Now that we've acquired the lock, make sure we still have valid state for this operation. - ValidateActiveOnConnection(connection); + // Now that we've acquired the lock, make sure we still have valid state for this operation. + ValidateActiveOnConnection(connection); - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); - returnValue = connection.PromotedDTCToken; + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); + returnValue = connection.PromotedDTCToken; - // For Global Transactions, we need to set the Transaction Id since we use a Non-MSDTC Promoter type. - if (connection.IsGlobalTransaction) + // For Global Transactions, we need to set the Transaction Id since we use a Non-MSDTC Promoter type. + if (connection.IsGlobalTransaction) + { + if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) { - if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) - { - throw SQL.UnsupportedSysTxForGlobalTransactions(); - } - - if (!connection.IsGlobalTransactionsEnabledForServer) - { - throw SQL.GlobalTransactionsNotEnabled(); - } + throw SQL.UnsupportedSysTxForGlobalTransactions(); + } - SysTxForGlobalTransactions.SetDistributedTransactionIdentifier.Invoke(_atomicTransaction, new object[] { this, GetGlobalTxnIdentifierFromToken() }); + if (!connection.IsGlobalTransactionsEnabledForServer) + { + throw SQL.GlobalTransactionsNotEnabled(); } - promoteException = null; + SysTxForGlobalTransactions.SetDistributedTransactionIdentifier.Invoke(_atomicTransaction, new object[] { this, GetGlobalTxnIdentifierFromToken() }); } - catch (SqlException e) - { - promoteException = e; - ADP.TraceExceptionWithoutRethrow(e); + promoteException = null; + } + catch (SqlException e) + { + promoteException = e; - // Doom the connection, to make sure that the transaction is - // eventually rolled back. - // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event - connection.DoomThisConnection(); - } - catch (InvalidOperationException e) - { - promoteException = e; - ADP.TraceExceptionWithoutRethrow(e); - connection.DoomThisConnection(); - } + ADP.TraceExceptionWithoutRethrow(e); + + // Doom the connection, to make sure that the transaction is + // eventually rolled back. + // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event + connection.DoomThisConnection(); + } + catch (InvalidOperationException e) + { + promoteException = e; + ADP.TraceExceptionWithoutRethrow(e); + connection.DoomThisConnection(); } - } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - usersConnection.Abort(e); - throw; } //Throw exception only if Transaction is still active and not yet aborted. @@ -256,74 +216,54 @@ public void Rollback(SinglePhaseEnlistment enlistment) { SqlConnection usersConnection = connection.Connection; SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Rollback | RES | CPOOL | Object Id {0}, Client Connection Id {1}, rolling back transaction.", ObjectID, usersConnection?.ClientConnectionId); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try + + lock (connection) { - lock (connection) + try { - try - { - // Now that we've acquired the lock, make sure we still have valid state for this operation. - ValidateActiveOnConnection(connection); - _active = false; // set to inactive first, doesn't matter how the execute completes, this transaction is done. - _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event + // Now that we've acquired the lock, make sure we still have valid state for this operation. + ValidateActiveOnConnection(connection); + _active = false; // set to inactive first, doesn't matter how the execute completes, this transaction is done. + _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event - // If we haven't already rolled back (or aborted) then tell the SQL Server to roll back - if (!_internalTransaction.IsAborted) - { - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); - } - } - catch (SqlException e) + // If we haven't already rolled back (or aborted) then tell the SQL Server to roll back + if (!_internalTransaction.IsAborted) { - ADP.TraceExceptionWithoutRethrow(e); - - // Doom the connection, to make sure that the transaction is - // eventually rolled back. - // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event - connection.DoomThisConnection(); - - // Unlike SinglePhaseCommit, a rollback is a rollback, regardless - // of how it happens, so SysTx won't throw an exception, and we - // don't want to throw an exception either, because SysTx isn't - // handling it and it may create a fail fast scenario. In the end, - // there is no way for us to communicate to the consumer that this - // failed for more serious reasons than usual. - // - // This is a bit like "should you throw if Close fails", however, - // it only matters when you really need to know. In that case, - // we have the tracing that we're doing to fallback on for the - // investigation. - } - catch (InvalidOperationException e) - { - ADP.TraceExceptionWithoutRethrow(e); - connection.DoomThisConnection(); + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); } } - - // it doesn't matter whether the rollback succeeded or not, we presume - // that the transaction is aborted, because it will be eventually. - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); - enlistment.Aborted(); - } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - usersConnection.Abort(e); - throw; + catch (SqlException e) + { + ADP.TraceExceptionWithoutRethrow(e); + + // Doom the connection, to make sure that the transaction is + // eventually rolled back. + // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event + connection.DoomThisConnection(); + + // Unlike SinglePhaseCommit, a rollback is a rollback, regardless + // of how it happens, so SysTx won't throw an exception, and we + // don't want to throw an exception either, because SysTx isn't + // handling it and it may create a fail fast scenario. In the end, + // there is no way for us to communicate to the consumer that this + // failed for more serious reasons than usual. + // + // This is a bit like "should you throw if Close fails", however, + // it only matters when you really need to know. In that case, + // we have the tracing that we're doing to fallback on for the + // investigation. + } + catch (InvalidOperationException e) + { + ADP.TraceExceptionWithoutRethrow(e); + connection.DoomThisConnection(); + } } + + // it doesn't matter whether the rollback succeeded or not, we presume + // that the transaction is aborted, because it will be eventually. + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + enlistment.Aborted(); } else { @@ -343,109 +283,88 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) { SqlConnection usersConnection = connection.Connection; SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.SinglePhaseCommit | RES | CPOOL | Object Id {0}, Client Connection Id {1}, committing transaction.", ObjectID, usersConnection?.ClientConnectionId); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try + // If the connection is doomed, we can be certain that the + // transaction will eventually be rolled back or has already been aborted externally, and we shouldn't + // attempt to commit it. + if (connection.IsConnectionDoomed) { - // If the connection is doomed, we can be certain that the - // transaction will eventually be rolled back or has already been aborted externally, and we shouldn't - // attempt to commit it. - if (connection.IsConnectionDoomed) + lock (connection) { - lock (connection) - { - _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. - _connection = null; - } - - enlistment.Aborted(SQL.ConnectionDoomed()); + _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. + _connection = null; } - else + + enlistment.Aborted(SQL.ConnectionDoomed()); + } + else + { + Exception commitException; + lock (connection) { - Exception commitException; - lock (connection) + try { - try - { - // Now that we've acquired the lock, make sure we still have valid state for this operation. - ValidateActiveOnConnection(connection); + // Now that we've acquired the lock, make sure we still have valid state for this operation. + ValidateActiveOnConnection(connection); - _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. - _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event + _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. + _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); - commitException = null; - } - catch (SqlException e) - { - commitException = e; + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); + commitException = null; + } + catch (SqlException e) + { + commitException = e; - ADP.TraceExceptionWithoutRethrow(e); + ADP.TraceExceptionWithoutRethrow(e); - // Doom the connection, to make sure that the transaction is - // eventually rolled back. - // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event - connection.DoomThisConnection(); - } - catch (InvalidOperationException e) - { - commitException = e; - ADP.TraceExceptionWithoutRethrow(e); - connection.DoomThisConnection(); - } + // Doom the connection, to make sure that the transaction is + // eventually rolled back. + // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event + connection.DoomThisConnection(); } - if (commitException != null) + catch (InvalidOperationException e) { - // connection.ExecuteTransaction failed with exception - if (_internalTransaction.IsCommitted) - { - // Even though we got an exception, the transaction - // was committed by the server. - enlistment.Committed(); - } - else if (_internalTransaction.IsAborted) - { - // The transaction was aborted, report that to - // System.Transactions. - enlistment.Aborted(commitException); - } - else - { - // The transaction is still active, we cannot - // know the state of the transaction. - enlistment.InDoubt(commitException); - } - - // We eat the exception. This is called on the SysTx - // thread, not the applications thread. If we don't - // eat the exception an UnhandledException will occur, - // causing the process to FailFast. + commitException = e; + ADP.TraceExceptionWithoutRethrow(e); + connection.DoomThisConnection(); } - - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); - if (commitException == null) + } + if (commitException != null) + { + // connection.ExecuteTransaction failed with exception + if (_internalTransaction.IsCommitted) { - // connection.ExecuteTransaction succeeded + // Even though we got an exception, the transaction + // was committed by the server. enlistment.Committed(); } + else if (_internalTransaction.IsAborted) + { + // The transaction was aborted, report that to + // System.Transactions. + enlistment.Aborted(commitException); + } + else + { + // The transaction is still active, we cannot + // know the state of the transaction. + enlistment.InDoubt(commitException); + } + + // We eat the exception. This is called on the SysTx + // thread, not the applications thread. If we don't + // eat the exception an UnhandledException will occur, + // causing the process to FailFast. + } + + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + if (commitException == null) + { + // connection.ExecuteTransaction succeeded + enlistment.Committed(); } - } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - usersConnection.Abort(e); - throw; } } else From cf11b5ce065f81314283f95a0a5f95db3ed5dced Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:43:19 -0500 Subject: [PATCH 11/21] Remove CER from SqlInternalConnection --- .../Data/SqlClient/SqlInternalConnection.cs | 76 +------------------ 1 file changed, 1 insertion(+), 75 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs index 742c8b2865..03800a627f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs @@ -215,14 +215,8 @@ override public DbTransaction BeginTransaction(System.Data.IsolationLevel iso) virtual internal SqlTransaction BeginSqlTransaction(System.Data.IsolationLevel iso, string transactionName, bool shouldReconnect) { SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { - bestEffortCleanupTarget = GetBestEffortCleanupTarget(Connection); - statistics = SqlStatistics.StartTimer(Connection.Statistics); #if NETFRAMEWORK @@ -247,24 +241,6 @@ virtual internal SqlTransaction BeginSqlTransaction(System.Data.IsolationLevel i transaction.InternalTransaction.RestoreBrokenConnection = false; return transaction; } - catch (OutOfMemoryException e) - { - Connection.Abort(e); - throw; - } - catch (StackOverflowException e) - { - Connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - Connection.Abort(e); -#if NETFRAMEWORK - BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } finally { SqlStatistics.StopTimer(statistics); @@ -303,16 +279,10 @@ override protected DbReferenceCollection CreateReferenceCollection() override protected void Deactivate() { - TdsParser bestEffortCleanupTarget = null; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlInternalConnection.Deactivate | ADV | Object Id {0} deactivating, Client Connection Id {1}", ObjectID, Connection?.ClientConnectionId); - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection); - SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection; if (referenceCollection != null) { @@ -322,24 +292,6 @@ override protected void Deactivate() // Invoke subclass-specific deactivation logic InternalDeactivate(); } - catch (OutOfMemoryException) - { - DoomThisConnection(); - throw; - } - catch (StackOverflowException) - { - DoomThisConnection(); - throw; - } - catch (System.Threading.ThreadAbortException) - { - DoomThisConnection(); -#if NETFRAMEWORK - BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } catch (Exception e) { if (!ADP.IsCatchableExceptionType(e)) @@ -624,33 +576,7 @@ override public void EnlistTransaction(Transaction transaction) // enlist in the user specified distributed transaction. This // behavior matches OLEDB and ODBC. - TdsParser bestEffortCleanupTarget = null; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif // NETFRAMEWORK - try - { - bestEffortCleanupTarget = GetBestEffortCleanupTarget(Connection); - Enlist(transaction); - } - catch (OutOfMemoryException e) - { - Connection.Abort(e); - throw; - } - catch (StackOverflowException e) - { - Connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - Connection.Abort(e); -#if NETFRAMEWORK - BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } + Enlist(transaction); } abstract internal void ExecuteTransaction(TransactionRequest transactionRequest, string name, System.Data.IsolationLevel iso, SqlInternalTransaction internalTransaction, bool isDelegateControlRequest); From b093c7e005073ce01f5c318c9c8ba7e39c6c3346 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:44:54 -0500 Subject: [PATCH 12/21] Remove CER from SqlInternalConnectionTds --- .../SqlClient/SqlInternalConnectionTds.cs | 61 +++---------------- 1 file changed, 9 insertions(+), 52 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 24082bf046..cb9c533cbe 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 @@ -539,9 +539,6 @@ 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); @@ -573,21 +570,6 @@ internal SqlInternalConnectionTds( } } } - catch (System.OutOfMemoryException) - { - DoomThisConnection(); - throw; - } - catch (System.StackOverflowException) - { - DoomThisConnection(); - throw; - } - catch (System.Threading.ThreadAbortException) - { - DoomThisConnection(); - throw; - } finally { ThreadHasParserLockForClose = false; @@ -2136,39 +2118,18 @@ internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = try { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try + Task reconnectTask = parent.ValidateAndReconnect(() => { - 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) - { - DoomThisConnection(); - throw; - } - catch (System.StackOverflowException) - { - DoomThisConnection(); - throw; - } - catch (System.Threading.ThreadAbortException) + ThreadHasParserLockForClose = false; + _parserLock.Release(); + releaseConnectionLock = false; + }, timeout); + if (reconnectTask != null) { - DoomThisConnection(); - throw; + AsyncHelper.WaitForCompletion(reconnectTask, timeout); + return true; } + return false; } finally { @@ -2475,10 +2436,6 @@ internal bool TryGetFedAuthTokenLocked(SqlFedAuthInfo fedAuthInfo, DbConnectionP // Variable which indicates if we did indeed manage to acquire the lock on the authentication context, to try update it. bool authenticationContextLocked = false; - // Prepare CER to ensure the lock on authentication context is released. -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { // Try to obtain a lock on the context. If acquired, this thread got the opportunity to update. From 7c8cdd8f3bbede3f197605e5415989ce6ca28fca Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:47:05 -0500 Subject: [PATCH 13/21] Remove CER from TdsParser (netcore) --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 164 +++++------------- 1 file changed, 40 insertions(+), 124 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 8480409042..003303d956 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -4641,84 +4641,63 @@ internal int GetCodePage(SqlCollation collation, TdsParserStateObject stateObj) internal void DrainData(TdsParserStateObject stateObj) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { - try + SqlDataReader.SharedState sharedState = stateObj._readerState; + if (sharedState != null && sharedState._dataReady) { - SqlDataReader.SharedState sharedState = stateObj._readerState; - if (sharedState != null && sharedState._dataReady) + _SqlMetaDataSet metadata = stateObj._cleanupMetaData; + if (stateObj._partialHeaderBytesRead > 0) { - _SqlMetaDataSet metadata = stateObj._cleanupMetaData; - if (stateObj._partialHeaderBytesRead > 0) + if (stateObj.TryProcessHeader() != TdsOperationStatus.Done) { - if (stateObj.TryProcessHeader() != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } + throw SQL.SynchronousCallMayNotPend(); } - if (0 == sharedState._nextColumnHeaderToRead) + } + if (0 == sharedState._nextColumnHeaderToRead) + { + // i. user called read but didn't fetch anything + if (stateObj.Parser.TrySkipRow(stateObj._cleanupMetaData, stateObj) != TdsOperationStatus.Done) { - // i. user called read but didn't fetch anything - if (stateObj.Parser.TrySkipRow(stateObj._cleanupMetaData, stateObj) != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } + throw SQL.SynchronousCallMayNotPend(); } - else + } + else + { + // iia. if we still have bytes left from a partially read column, skip + if (sharedState._nextColumnDataToRead < sharedState._nextColumnHeaderToRead) { - // iia. if we still have bytes left from a partially read column, skip - if (sharedState._nextColumnDataToRead < sharedState._nextColumnHeaderToRead) + if ((sharedState._nextColumnHeaderToRead > 0) && (metadata[sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) { - if ((sharedState._nextColumnHeaderToRead > 0) && (metadata[sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) + if (stateObj._longlen != 0) { - if (stateObj._longlen != 0) + if (TrySkipPlpValue(ulong.MaxValue, stateObj, out _) != TdsOperationStatus.Done) { - if (TrySkipPlpValue(ulong.MaxValue, stateObj, out _) != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } + throw SQL.SynchronousCallMayNotPend(); } } + } - else if (0 < sharedState._columnDataBytesRemaining) + else if (0 < sharedState._columnDataBytesRemaining) + { + if (stateObj.TrySkipLongBytes(sharedState._columnDataBytesRemaining) != TdsOperationStatus.Done) { - if (stateObj.TrySkipLongBytes(sharedState._columnDataBytesRemaining) != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } + throw SQL.SynchronousCallMayNotPend(); } } + } - // Read the remaining values off the wire for this row - if (stateObj.Parser.TrySkipRow(metadata, sharedState._nextColumnHeaderToRead, stateObj) != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } + // Read the remaining values off the wire for this row + if (stateObj.Parser.TrySkipRow(metadata, sharedState._nextColumnHeaderToRead, stateObj) != TdsOperationStatus.Done) + { + throw SQL.SynchronousCallMayNotPend(); } } - Run(RunBehavior.Clean, null, null, null, stateObj); } - catch - { - _connHandler.DoomThisConnection(); - throw; - } - } - catch (OutOfMemoryException) - { - _connHandler.DoomThisConnection(); - throw; - } - catch (StackOverflowException) - { - _connHandler.DoomThisConnection(); - throw; + Run(RunBehavior.Clean, null, null, null, stateObj); } - catch (ThreadAbortException) + catch { _connHandler.DoomThisConnection(); throw; @@ -10214,28 +10193,7 @@ private void FinalizeExecuteRPC(TdsParserStateObject stateObj) private void TdsExecuteRPC_OnFailure(Exception exc, TdsParserStateObject stateObj) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - FailureCleanup(stateObj, exc); - } - catch (OutOfMemoryException) - { - _connHandler.DoomThisConnection(); - throw; - } - catch (StackOverflowException) - { - _connHandler.DoomThisConnection(); - throw; - } - catch (ThreadAbortException) - { - _connHandler.DoomThisConnection(); - throw; - } + FailureCleanup(stateObj, exc); } private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, TaskCompletionSource completion, bool releaseConnectionLock) @@ -10247,31 +10205,10 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T { Exception exc = tsk.Exception.InnerException; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { FailureCleanup(stateObj, tsk.Exception); } - catch (OutOfMemoryException e) - { - _connHandler.DoomThisConnection(); - completion.SetException(e); - throw; - } - catch (StackOverflowException e) - { - _connHandler.DoomThisConnection(); - completion.SetException(e); - throw; - } - catch (ThreadAbortException e) - { - _connHandler.DoomThisConnection(); - completion.SetException(e); - throw; - } catch (Exception e) { exc = e; @@ -11959,35 +11896,14 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati StripPreamble(buffer, ref offset, ref count); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - Task task = null; - if (count > 0) - { - _parser.WriteInt(count, _stateObj); // write length of chunk - task = _stateObj.WriteByteArray(buffer, count, offset, canAccumulate: false); - } - - return task ?? Task.CompletedTask; - } - catch (OutOfMemoryException) - { - _parser._connHandler.DoomThisConnection(); - throw; - } - catch (StackOverflowException) - { - _parser._connHandler.DoomThisConnection(); - throw; - } - catch (ThreadAbortException) + Task task = null; + if (count > 0) { - _parser._connHandler.DoomThisConnection(); - throw; + _parser.WriteInt(count, _stateObj); // write length of chunk + task = _stateObj.WriteByteArray(buffer, count, offset, canAccumulate: false); } + + return task ?? Task.CompletedTask; } internal static void ValidateWriteParameters(byte[] buffer, int offset, int count) From 42d37b6e96a5294419efe98b9fa499b3a41b38f6 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:48:04 -0500 Subject: [PATCH 14/21] Remove CER from TdsParserStateObject (netcore) --- .../Data/SqlClient/TdsParserStateObject.netcore.cs | 14 -------------- 1 file changed, 14 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 d7dc8b62f4..dd264155ec 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 @@ -17,17 +17,6 @@ namespace Microsoft.Data.SqlClient { internal abstract partial class TdsParserStateObject { - private struct RuntimeHelpers - { - /// - /// This is a no-op in netcore version. Only needed for merging with netfx codebase. - /// - [Conditional("NETFRAMEWORK")] - internal static void PrepareConstrainedRegions() - { - } - } - ////////////////// // Constructors // ////////////////// @@ -168,7 +157,6 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error) PacketHandle syncReadPacket = default; bool readFromNetwork = true; - RuntimeHelpers.PrepareConstrainedRegions(); bool shouldDecrement = false; try { @@ -317,7 +305,6 @@ public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error) return; } - RuntimeHelpers.PrepareConstrainedRegions(); bool processFinallyBlock = true; try { @@ -711,7 +698,6 @@ internal void SendAttention(bool mustTakeWriteLock = false, bool asyncClose = fa PacketHandle attnPacket = CreateAndSetAttentionPacket(); - RuntimeHelpers.PrepareConstrainedRegions(); try { // Dev11 #344723: SqlClient stress test suspends System_Data!Tcp::ReadSync via a call to SqlDataReader::Close From 7988457e3f740a885411c5102f815b6178ec0bb2 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 12:49:44 -0500 Subject: [PATCH 15/21] Remove CER from TdsParserStateObjectNative and LocalDbApi --- .../Data/SqlClient/TdsParserStateObjectNative.cs | 12 ++---------- .../Data/SqlClient/LocalDb/LocalDbApi.Windows.cs | 12 ------------ 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 4f2ccaab83..d625764aff 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -453,16 +453,8 @@ internal override void DisposePacketCache() { lock (_writePacketLockObject) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { } - finally - { - _writePacketCache.Dispose(); - // Do not set _writePacketCache to null, just in case a WriteAsyncCallback completes after this point - } + _writePacketCache.Dispose(); + // Do not set _writePacketCache to null, just in case a WriteAsyncCallback completes after this point } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDb/LocalDbApi.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDb/LocalDbApi.Windows.cs index c160af38d3..e64d4fcf2f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDb/LocalDbApi.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalDb/LocalDbApi.Windows.cs @@ -80,8 +80,6 @@ private static LocalDbCreateInstanceDelegate LocalDbCreateInstance { if (s_localDbCreateInstance is null) { - RuntimeHelpers.PrepareConstrainedRegions(); - lock (s_dllLock) { if (s_localDbCreateInstance is null) @@ -110,10 +108,6 @@ private static LocalDbFormatMessageDelegate LocalDbFormatMessage { if (s_localDbFormatMessage is null) { - #if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); - #endif - lock (s_dllLock) { if (s_localDbFormatMessage is null) @@ -142,10 +136,6 @@ private static IntPtr UserInstanceDllHandle { if (s_userInstanceDllHandle == IntPtr.Zero) { - #if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); - #endif - lock (s_dllLock) { if (s_userInstanceDllHandle == IntPtr.Zero) @@ -187,8 +177,6 @@ internal static void CreateLocalDbInstance(string instance) if (s_configurableInstances is null) { // load list of instances from configuration, mark them as not created - RuntimeHelpers.PrepareConstrainedRegions(); - lock (s_configLock) { if (s_configurableInstances is null) From e36a480fa73f914ff9d6306602705547fbec8f61 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 13:08:28 -0500 Subject: [PATCH 16/21] Remove CER from SqlCommand --- .../Microsoft/Data/SqlClient/SqlCommand.cs | 712 +++++------------- 1 file changed, 208 insertions(+), 504 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index d65a0b7522..3b14e9404e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -511,27 +511,11 @@ private SqlCommand(SqlCommand from) : this() { if (_activeConnection != value && _activeConnection != null) { - RuntimeHelpers.PrepareConstrainedRegions(); try { // cleanup Unprepare(); } - catch (System.OutOfMemoryException) - { - _activeConnection.InnerConnection.DoomThisConnection(); - throw; - } - catch (System.StackOverflowException) - { - _activeConnection.InnerConnection.DoomThisConnection(); - throw; - } - catch (System.Threading.ThreadAbortException) - { - _activeConnection.InnerConnection.DoomThisConnection(); - throw; - } catch (Exception) { // we do not really care about errors in unprepare (may be the old connection went bad) @@ -952,12 +936,8 @@ public override void Prepare() ValidateCommand(isAsync: false); bool processFinallyBlock = true; - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - // NOTE: The state object isn't actually needed for this, but it is still here for back-compat (since it does a bunch of checks) GetStateObject(); @@ -973,26 +953,6 @@ public override void Prepare() InternalPrepare(); } - catch (System.OutOfMemoryException e) - { - processFinallyBlock = false; - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - processFinallyBlock = false; - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - processFinallyBlock = false; - _activeConnection.Abort(e); - - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } catch (Exception e) { processFinallyBlock = ADP.IsCatchableExceptionType(e); @@ -1124,53 +1084,30 @@ public override void Cancel() return; } - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try + if (!_pendingCancel) { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - - if (!_pendingCancel) + // Do nothing if aleady pending. + // Before attempting actual cancel, set the _pendingCancel flag to false. + // This denotes to other thread before obtaining stateObject from the + // session pool that there is another thread wishing to cancel. + // The period in question is between entering the ExecuteAPI and obtaining + // a stateObject. + _pendingCancel = true; + + TdsParserStateObject stateObj = _stateObj; + if (stateObj != null) { - // Do nothing if aleady pending. - // Before attempting actual cancel, set the _pendingCancel flag to false. - // This denotes to other thread before obtaining stateObject from the - // session pool that there is another thread wishing to cancel. - // The period in question is between entering the ExecuteAPI and obtaining - // a stateObject. - _pendingCancel = true; - - TdsParserStateObject stateObj = _stateObj; - if (stateObj != null) - { - stateObj.Cancel(this); - } - else + stateObj.Cancel(this); + } + else + { + SqlDataReader reader = connection.FindLiveReader(this); + if (reader != null) { - SqlDataReader reader = connection.FindLiveReader(this); - if (reader != null) - { - reader.Cancel(this); - } + reader.Cancel(this); } } } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } } } finally @@ -1478,8 +1415,6 @@ private IAsyncResult BeginExecuteNonQueryInternal(CommandBehavior behavior, Asyn private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource completion) { // Read SNI does not have catches for async exceptions, handle here. - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); @@ -1487,22 +1422,6 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection); _stateObj.ReadSni(completion); } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } catch (Exception) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -1697,107 +1616,85 @@ private object InternalEndExecuteNonQuery( SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}", _activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, _activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress); - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try + VerifyEndExecuteState((Task)asyncResult, endMethod); + WaitForAsyncResults(asyncResult, isInternal); + + // If column encryption is enabled, also check the state after waiting for the task. + // It would be better to do this for all cases, but avoiding for compatibility reasons. + if (IsColumnEncryptionEnabled) { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - VerifyEndExecuteState((Task)asyncResult, endMethod); - WaitForAsyncResults(asyncResult, isInternal); + VerifyEndExecuteState((Task)asyncResult, endMethod, fullCheckForColumnEncryption: true); + } - // If column encryption is enabled, also check the state after waiting for the task. - // It would be better to do this for all cases, but avoiding for compatibility reasons. - if (IsColumnEncryptionEnabled) + bool processFinallyBlock = true; + try + { + // If this is not for internal usage, notify the dependency. + // If we have already initiated the end internally, the reader should be ready, so just return the rows affected. + if (!isInternal) { - VerifyEndExecuteState((Task)asyncResult, endMethod, fullCheckForColumnEncryption: true); - } + NotifyDependency(); - bool processFinallyBlock = true; - try - { - // If this is not for internal usage, notify the dependency. - // If we have already initiated the end internally, the reader should be ready, so just return the rows affected. - if (!isInternal) + if (_internalEndExecuteInitiated) { - NotifyDependency(); - - if (_internalEndExecuteInitiated) - { - Debug.Assert(_stateObj == null); + Debug.Assert(_stateObj == null); - // Reset the state since we exit early. - CachedAsyncState.ResetAsyncState(); + // Reset the state since we exit early. + CachedAsyncState.ResetAsyncState(); - return _rowsAffected; - } + return _rowsAffected; } + } - CheckThrowSNIException(); + CheckThrowSNIException(); - // only send over SQL Batch command if we are not a stored proc and have no parameters - if ((System.Data.CommandType.Text == this.CommandType) && (0 == GetParameterCount(_parameters))) + // only send over SQL Batch command if we are not a stored proc and have no parameters + if ((System.Data.CommandType.Text == this.CommandType) && (0 == GetParameterCount(_parameters))) + { + try { - try - { - Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); - TdsOperationStatus result = _stateObj.Parser.TryRun(RunBehavior.UntilDone, this, null, null, _stateObj, out _); - if (result != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } - } - finally + Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); + TdsOperationStatus result = _stateObj.Parser.TryRun(RunBehavior.UntilDone, this, null, null, _stateObj, out _); + if (result != TdsOperationStatus.Done) { - // Don't reset the state for internal End. The user End will do that eventually. - if (!isInternal) - { - CachedAsyncState.ResetAsyncState(); - } + throw SQL.SynchronousCallMayNotPend(); } } - else + finally { - // otherwise, use a full-fledged execute that can handle params and stored procs - SqlDataReader reader = CompleteAsyncExecuteReader(isInternal); - if (reader != null) + // Don't reset the state for internal End. The user End will do that eventually. + if (!isInternal) { - reader.Close(); + CachedAsyncState.ResetAsyncState(); } } } - catch (Exception e) - { - processFinallyBlock = ADP.IsCatchableExceptionType(e); - throw; - } - finally + else { - if (processFinallyBlock) + // otherwise, use a full-fledged execute that can handle params and stored procs + SqlDataReader reader = CompleteAsyncExecuteReader(isInternal); + if (reader != null) { - PutStateObject(); + reader.Close(); } } - - Debug.Assert(_stateObj == null, "non-null state object in EndExecuteNonQuery"); - return _rowsAffected; } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) + catch (Exception e) { - _activeConnection.Abort(e); + processFinallyBlock = ADP.IsCatchableExceptionType(e); throw; } - catch (System.Threading.ThreadAbortException e) + finally { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; + if (processFinallyBlock) + { + PutStateObject(); + } } + + Debug.Assert(_stateObj == null, "non-null state object in EndExecuteNonQuery"); + return _rowsAffected; } private Task InternalExecuteNonQuery( @@ -1817,92 +1714,70 @@ private Task InternalExecuteNonQuery( SqlStatistics statistics = Statistics; _rowsAffected = -1; - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try + // @devnote: this function may throw for an invalid connection + // @devnote: returns false for empty command text + if (!isRetry) { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - // @devnote: this function may throw for an invalid connection - // @devnote: returns false for empty command text - if (!isRetry) - { - ValidateCommand(isAsync, methodName); - } - CheckNotificationStateAndAutoEnlist(); // Only call after validate - requires non null connection! + ValidateCommand(isAsync, methodName); + } + CheckNotificationStateAndAutoEnlist(); // Only call after validate - requires non null connection! - Task task = null; + Task task = null; - // Always Encrypted generally operates only on parameterized queries. However enclave based Always encrypted also supports unparameterized queries - // We skip this block for enclave based always encrypted so that we can make a call to SQL Server to get the encryption information - if (!ShouldUseEnclaveBasedWorkflow && !_batchRPCMode && CommandType == CommandType.Text && GetParameterCount(_parameters) == 0) + // Always Encrypted generally operates only on parameterized queries. However enclave based Always encrypted also supports unparameterized queries + // We skip this block for enclave based always encrypted so that we can make a call to SQL Server to get the encryption information + if (!ShouldUseEnclaveBasedWorkflow && !_batchRPCMode && CommandType == CommandType.Text && GetParameterCount(_parameters) == 0) + { + Debug.Assert(!sendToPipe, "trying to send non-context command to pipe"); + if (statistics != null) { - Debug.Assert(!sendToPipe, "trying to send non-context command to pipe"); - if (statistics != null) + if (!IsDirty && IsPrepared) { - if (!IsDirty && IsPrepared) - { - statistics.SafeIncrement(ref statistics._preparedExecs); - } - else - { - statistics.SafeIncrement(ref statistics._unpreparedExecs); - } + statistics.SafeIncrement(ref statistics._preparedExecs); + } + else + { + statistics.SafeIncrement(ref statistics._unpreparedExecs); } + } - // We should never get here for a retry since we only have retries for parameters. - Debug.Assert(!isRetry); + // We should never get here for a retry since we only have retries for parameters. + Debug.Assert(!isRetry); - task = RunExecuteNonQueryTds(methodName, isAsync, timeout, asyncWrite); - } - else - { - // otherwise, use a full-fledged execute that can handle params and stored procs - Debug.Assert(!sendToPipe, "Trying to send non-context command to pipe"); - SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteNonQuery | INFO | Object Id {0}, RPC execute method name {1}, isAsync {2}, isRetry {3}", ObjectID, methodName, isAsync, isRetry); + task = RunExecuteNonQueryTds(methodName, isAsync, timeout, asyncWrite); + } + else + { + // otherwise, use a full-fledged execute that can handle params and stored procs + Debug.Assert(!sendToPipe, "Trying to send non-context command to pipe"); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteNonQuery | INFO | Object Id {0}, RPC execute method name {1}, isAsync {2}, isRetry {3}", ObjectID, methodName, isAsync, isRetry); - SqlDataReader reader = RunExecuteReader( - CommandBehavior.Default, - RunBehavior.UntilDone, - returnStream: false, - completion, - timeout, - out task, - out usedCache, - asyncWrite, - isRetry, - methodName); - - if (reader != null) + SqlDataReader reader = RunExecuteReader( + CommandBehavior.Default, + RunBehavior.UntilDone, + returnStream: false, + completion, + timeout, + out task, + out usedCache, + asyncWrite, + isRetry, + methodName); + + if (reader != null) + { + if (task != null) { - if (task != null) - { - task = AsyncHelper.CreateContinuationTask(task, () => reader.Close()); - } - else - { - reader.Close(); - } + task = AsyncHelper.CreateContinuationTask(task, () => reader.Close()); + } + else + { + reader.Close(); } } - Debug.Assert(isAsync || _stateObj == null, "non-null state object in InternalExecuteNonQuery"); - return task; - } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; } + Debug.Assert(isAsync || _stateObj == null, "non-null state object in InternalExecuteNonQuery"); + return task; } /// @@ -2075,34 +1950,12 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource { Debug.Assert(completion != null, "Completion source should not be null"); // Read SNI does not have catches for async exceptions, handle here. - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); // must finish caching information before ReadSni which can activate the callback before returning CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteXmlReader), _activeConnection); _stateObj.ReadSni(completion); } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - completion.TrySetException(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - completion.TrySetException(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - completion.TrySetException(e); - throw; - } catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -2289,8 +2142,6 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) _pendingCancel = false; SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); bool success = false; int? sqlExceptionNumber = null; @@ -2299,7 +2150,6 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) try { WriteBeginExecuteEvent(); - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); statistics = SqlStatistics.StartTimer(Statistics); SqlDataReader result = IsProviderRetriable ? RunExecuteReaderWithRetry(behavior, RunBehavior.ReturnImmediately, returnStream: true) : @@ -2312,22 +2162,6 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) sqlExceptionNumber = e.Number; throw; } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } finally { SqlStatistics.StopTimer(statistics); @@ -2734,34 +2568,12 @@ private void BeginExecuteReaderInternalReadStage(TaskCompletionSource co Debug.Assert(completion != null, "CompletionSource should not be null"); SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteReaderInternalReadStage | INFO | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); // Read SNI does not have catches for async exceptions, handle here. - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); // must finish caching information before ReadSni which can activate the callback before returning CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection); _stateObj.ReadSni(completion); } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - completion.TrySetException(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - completion.TrySetException(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - completion.TrySetException(e); - throw; - } catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -2789,31 +2601,9 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool is CheckThrowSNIException(); - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - SqlDataReader reader = CompleteAsyncExecuteReader(isInternal); - Debug.Assert(_stateObj == null, "non-null state object in InternalEndExecuteReader"); - return reader; - } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } + SqlDataReader reader = CompleteAsyncExecuteReader(isInternal); + Debug.Assert(_stateObj == null, "non-null state object in InternalEndExecuteReader"); + return reader; } /// @@ -4025,11 +3815,8 @@ private void PrepareForTransparentEncryption( // Used in _batchRPCMode to maintain a map of describe parameter encryption RPC requests (Keys) and their corresponding original RPC requests (Values). ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap = null; - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); try { // Fetch the encryption information that applies to any of the input parameters. @@ -4079,7 +3866,6 @@ private void PrepareForTransparentEncryption( bool processFinallyBlockAsync = true; bool decrementAsyncCountInFinallyBlockAsync = true; - RuntimeHelpers.PrepareConstrainedRegions(); try { // Check for any exceptions on network write, before reading. @@ -4154,7 +3940,6 @@ private void PrepareForTransparentEncryption( bool processFinallyBlockAsync = true; bool decrementAsyncCountInFinallyBlockAsync = true; - RuntimeHelpers.PrepareConstrainedRegions(); try { @@ -4234,22 +4019,6 @@ private void PrepareForTransparentEncryption( describeParameterEncryptionDataReader: describeParameterEncryptionDataReader); } } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } catch (Exception e) { if (CachedAsyncState != null) @@ -4940,59 +4709,102 @@ internal SqlDataReader RunExecuteReader( CheckNotificationStateAndAutoEnlist(); // Only call after validate - requires non null connection! - TdsParser bestEffortCleanupTarget = null; // This section needs to occur AFTER ValidateCommand - otherwise it will AV without a connection. - RuntimeHelpers.PrepareConstrainedRegions(); - try + SqlStatistics statistics = Statistics; + if (statistics != null) { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - SqlStatistics statistics = Statistics; - if (statistics != null) + if ((!this.IsDirty && this.IsPrepared && !_hiddenPrepare) + || (this.IsPrepared && _execType == EXECTYPE.PREPAREPENDING)) { - if ((!this.IsDirty && this.IsPrepared && !_hiddenPrepare) - || (this.IsPrepared && _execType == EXECTYPE.PREPAREPENDING)) - { - statistics.SafeIncrement(ref statistics._preparedExecs); - } - else - { - statistics.SafeIncrement(ref statistics._unpreparedExecs); - } + statistics.SafeIncrement(ref statistics._preparedExecs); } + else + { + statistics.SafeIncrement(ref statistics._unpreparedExecs); + } + } - // Reset the encryption related state of the command and its parameters. - ResetEncryptionState(); + // Reset the encryption related state of the command and its parameters. + ResetEncryptionState(); - if (IsColumnEncryptionEnabled) + if (IsColumnEncryptionEnabled) + { + Task returnTask = null; + PrepareForTransparentEncryption(isAsync, timeout, completion, out returnTask, asyncWrite && isAsync, out usedCache, isRetry); + Debug.Assert(usedCache || (isAsync == (returnTask != null)), @"if we didn't use the cache, returnTask should be null if and only if async is false."); + + long firstAttemptStart = ADP.TimerCurrent(); + + try { - Task returnTask = null; - PrepareForTransparentEncryption(isAsync, timeout, completion, out returnTask, asyncWrite && isAsync, out usedCache, isRetry); - Debug.Assert(usedCache || (isAsync == (returnTask != null)), @"if we didn't use the cache, returnTask should be null if and only if async is false."); + return RunExecuteReaderTdsWithTransparentParameterEncryption( + cmdBehavior, + runBehavior, + returnStream, + isAsync, + timeout, + out task, + asyncWrite && isAsync, + isRetry: isRetry, + ds: null, + describeParameterEncryptionTask: returnTask); + } - long firstAttemptStart = ADP.TimerCurrent(); + catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) + { + if (isRetry) + { + throw; + } - try + // Retry if the command failed with appropriate error. + // First invalidate the entry from the cache, so that we refresh our encryption MD. + SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); + + InvalidateEnclaveSession(); + + return RunExecuteReader( + cmdBehavior, + runBehavior, + returnStream, + completion, + TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), + out task, + out usedCache, + isAsync, + isRetry: true, + method: method); + } + + catch (SqlException ex) + { + // We only want to retry once, so don't retry if we are already in retry. + // If we didn't use the cache, we don't want to retry. + if (isRetry || (!usedCache && !ShouldUseEnclaveBasedWorkflow)) { - return RunExecuteReaderTdsWithTransparentParameterEncryption( - cmdBehavior, - runBehavior, - returnStream, - isAsync, - timeout, - out task, - asyncWrite && isAsync, - isRetry: isRetry, - ds: null, - describeParameterEncryptionTask: returnTask); + throw; } - catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) + bool shouldRetry = false; + + // Check if we have an error indicating that we can retry. + for (int i = 0; i < ex.Errors.Count; i++) { - if (isRetry) + + if ((usedCache && (ex.Errors[i].Number == TdsEnums.TCE_CONVERSION_ERROR_CLIENT_RETRY)) || + (ShouldUseEnclaveBasedWorkflow && (ex.Errors[i].Number == TdsEnums.TCE_ENCLAVE_INVALID_SESSION_HANDLE))) { - throw; + shouldRetry = true; + break; } + } + if (!shouldRetry) + { + throw; + } + else + { // Retry if the command failed with appropriate error. // First invalidate the entry from the cache, so that we refresh our encryption MD. SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); @@ -5011,76 +4823,11 @@ internal SqlDataReader RunExecuteReader( isRetry: true, method: method); } - - catch (SqlException ex) - { - // We only want to retry once, so don't retry if we are already in retry. - // If we didn't use the cache, we don't want to retry. - if (isRetry || (!usedCache && !ShouldUseEnclaveBasedWorkflow)) - { - throw; - } - - bool shouldRetry = false; - - // Check if we have an error indicating that we can retry. - for (int i = 0; i < ex.Errors.Count; i++) - { - - if ((usedCache && (ex.Errors[i].Number == TdsEnums.TCE_CONVERSION_ERROR_CLIENT_RETRY)) || - (ShouldUseEnclaveBasedWorkflow && (ex.Errors[i].Number == TdsEnums.TCE_ENCLAVE_INVALID_SESSION_HANDLE))) - { - shouldRetry = true; - break; - } - } - - if (!shouldRetry) - { - throw; - } - else - { - // Retry if the command failed with appropriate error. - // First invalidate the entry from the cache, so that we refresh our encryption MD. - SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); - - InvalidateEnclaveSession(); - - return RunExecuteReader( - cmdBehavior, - runBehavior, - returnStream, - completion, - TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), - out task, - out usedCache, - isAsync, - isRetry: true, - method: method); - } - } } - else - { - return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, isAsync, timeout, out task, asyncWrite && isAsync, isRetry: isRetry); - } - } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) + else { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; + return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, isAsync, timeout, out task, asyncWrite && isAsync, isRetry: isRetry); } } @@ -5674,31 +5421,10 @@ private void ValidateCommand(bool isAsync, [CallerMemberName] string method = "" ValidateAsyncCommand(); - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - // close any non MARS dead readers, if applicable, and then throw if still busy. - // Throw if we have a live reader on this command - _activeConnection.ValidateConnectionForExecute(method, this); - } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } + // close any non MARS dead readers, if applicable, and then throw if still busy. + // Throw if we have a live reader on this command + _activeConnection.ValidateConnectionForExecute(method, this); + // Check to see if the currently set transaction has completed. If so, // null out our local reference. if (_transaction != null && _transaction.Connection == null) @@ -5790,29 +5516,7 @@ private void GetStateObject(TdsParser parser = null) private void ReliablePutStateObject() { - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - PutStateObject(); - } - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _activeConnection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } + PutStateObject(); } private void PutStateObject() From ba4291254ec89028888416b9257a8f8857547a9d Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 13:12:59 -0500 Subject: [PATCH 17/21] Remove CER from SqlConnection --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 109 ++++-------------- 1 file changed, 22 insertions(+), 87 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 24cd7dbe28..3e7dde1850 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1245,31 +1245,12 @@ public override void ChangeDatabase(string database) SqlStatistics statistics = null; RepairInnerConnection(); SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID{0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this); statistics = SqlStatistics.StartTimer(Statistics); InnerConnection.ChangeDatabase(database); } - catch (System.OutOfMemoryException e) - { - Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } finally { SqlStatistics.StopTimer(statistics); @@ -1315,12 +1296,9 @@ public override void Close() SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this); statistics = SqlStatistics.StartTimer(Statistics); Task reconnectTask = _currentReconnectionTask; @@ -1346,22 +1324,6 @@ public override void Close() _statistics._closeTimestamp = ADP.TimerCurrent(); } } - catch (System.OutOfMemoryException ex) - { - Abort(ex); - throw; - } - catch (System.StackOverflowException ex) - { - Abort(ex); - throw; - } - catch (System.Threading.ThreadAbortException ex) - { - Abort(ex); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } finally { SqlStatistics.StopTimer(statistics); @@ -1436,7 +1398,6 @@ public void Open(SqlConnectionOverrides overrides) } SqlStatistics statistics = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -1709,7 +1670,6 @@ private Task InternalOpenAsync(SqlConnectionOverrides overrides, CancellationTok } SqlStatistics statistics = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -1791,7 +1751,6 @@ internal void Retry(Task retryTask) try { SqlStatistics statistics = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { statistics = SqlStatistics.StartTimer(_parent.Statistics); @@ -1897,64 +1856,40 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec private bool TryOpenInner(TaskCompletionSource retry) { - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try + if (ForceNewConnection) { - if (ForceNewConnection) + if (!InnerConnection.TryReplaceConnection(this, ConnectionFactory, retry, UserConnectionOptions)) { - if (!InnerConnection.TryReplaceConnection(this, ConnectionFactory, retry, UserConnectionOptions)) - { - return false; - } + return false; } - else + } + else + { + if (!InnerConnection.TryOpenConnection(this, ConnectionFactory, retry, UserConnectionOptions)) { - if (!InnerConnection.TryOpenConnection(this, ConnectionFactory, retry, UserConnectionOptions)) - { - return false; - } + return false; } - // does not require GC.KeepAlive(this) because of OnStateChange - - // GetBestEffortCleanup must happen AFTER OpenConnection to get the correct target. - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this); - - var tdsInnerConnection = (SqlInternalConnectionTds)InnerConnection; - Debug.Assert(tdsInnerConnection.Parser != null, "Where's the parser?"); + } + // does not require GC.KeepAlive(this) because of OnStateChange - if (!tdsInnerConnection.ConnectionOptions.Pooling) - { - // For non-pooled connections, we need to make sure that the finalizer does actually run to avoid leaking SNI handles - GC.ReRegisterForFinalize(this); - } + var tdsInnerConnection = (SqlInternalConnectionTds)InnerConnection; + Debug.Assert(tdsInnerConnection.Parser != null, "Where's the parser?"); - if (StatisticsEnabled) - { - _statistics._openTimestamp = ADP.TimerCurrent(); - tdsInnerConnection.Parser.Statistics = _statistics; - } - else - { - tdsInnerConnection.Parser.Statistics = null; - _statistics = null; // in case of previous Open/Close/reset_CollectStats sequence - } - } - catch (System.OutOfMemoryException e) + if (!tdsInnerConnection.ConnectionOptions.Pooling) { - Abort(e); - throw; + // For non-pooled connections, we need to make sure that the finalizer does actually run to avoid leaking SNI handles + GC.ReRegisterForFinalize(this); } - catch (System.StackOverflowException e) + + if (StatisticsEnabled) { - Abort(e); - throw; + _statistics._openTimestamp = ADP.TimerCurrent(); + tdsInnerConnection.Parser.Statistics = _statistics; } - catch (System.Threading.ThreadAbortException e) + else { - Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; + tdsInnerConnection.Parser.Statistics = null; + _statistics = null; // in case of previous Open/Close/reset_CollectStats sequence } return true; From d7fccff7d5343eb63dc3449635949ff2bbac4473 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 13:17:12 -0500 Subject: [PATCH 18/21] Remove CER from SqlInternalConnectionTds.cs and SqlDependencyListener.cs --- .../SqlClient/SqlInternalConnectionTds.cs | 55 +++---------------- .../Data/SqlClient/SqlDependencyListener.cs | 1 - 2 files changed, 9 insertions(+), 47 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 38118dda72..e57e65648b 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 @@ -549,7 +549,6 @@ 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 { _timeout = TimeoutTimer.StartSecondsTimeout(connectionOptions.ConnectTimeout); @@ -581,21 +580,6 @@ internal SqlInternalConnectionTds( } } } - catch (System.OutOfMemoryException) - { - DoomThisConnection(); - throw; - } - catch (System.StackOverflowException) - { - DoomThisConnection(); - throw; - } - catch (System.Threading.ThreadAbortException) - { - DoomThisConnection(); - throw; - } finally { ThreadHasParserLockForClose = false; @@ -2187,37 +2171,18 @@ internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = try { - RuntimeHelpers.PrepareConstrainedRegions(); - try + Task reconnectTask = parent.ValidateAndReconnect(() => { - 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) - { - DoomThisConnection(); - throw; - } - catch (System.StackOverflowException) - { - DoomThisConnection(); - throw; - } - catch (System.Threading.ThreadAbortException) + ThreadHasParserLockForClose = false; + _parserLock.Release(); + releaseConnectionLock = false; + }, timeout); + if (reconnectTask != null) { - DoomThisConnection(); - throw; + AsyncHelper.WaitForCompletion(reconnectTask, timeout); + return true; } + return false; } finally { @@ -2520,8 +2485,6 @@ internal bool TryGetFedAuthTokenLocked(SqlFedAuthInfo fedAuthInfo, DbConnectionP // Variable which indicates if we did indeed manage to acquire the lock on the authentication context, to try update it. bool authenticationContextLocked = false; - // Prepare CER to ensure the lock on authentication context is released. - RuntimeHelpers.PrepareConstrainedRegions(); try { // Try to obtain a lock on the context. If acquired, this thread got the opportunity to update. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs index 89d54e3196..b3fb3e38fa 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs @@ -691,7 +691,6 @@ private void Restart(object unused) if (_hashHelper.Identity != null) { // Only impersonate if Integrated Security. WindowsImpersonationContext context = null; - RuntimeHelpers.PrepareConstrainedRegions(); // CER for context.Undo. try { context = _windowsIdentity.Impersonate(); From b88fb551de3e476a6767001ec370a47ee695cc7b Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 13:24:08 -0500 Subject: [PATCH 19/21] Cleanup the rest of CER --- .../Microsoft/Data/SqlClient/SqlCommand.cs | 1 - .../src/Microsoft/Data/SqlClient/TdsParser.cs | 186 +++++------------- .../Data/SqlClient/TdsParser.netfx.cs | 23 +-- .../SqlClient/TdsParserStateObject.netfx.cs | 41 ++-- .../SqlClient/TdsParserStateObjectNative.cs | 12 +- .../SqlDataSourceEnumeratorNativeHelper.cs | 22 +-- .../SqlClient/TdsParserSafeHandles.Windows.cs | 94 ++++----- .../Data/SqlClient/TdsParserStateObject.cs | 45 ++--- 8 files changed, 135 insertions(+), 289 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 3b14e9404e..fea56991e7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -1417,7 +1417,6 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource // Read SNI does not have catches for async exceptions, handle here. try { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); // must finish caching information before ReadSni which can activate the callback before returning CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection); _stateObj.ReadSni(completion); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 3ad9c84e78..146bfec616 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -689,21 +689,16 @@ internal void EnableMars() IntPtr temp = IntPtr.Zero; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { } - finally - { - _pMarsPhysicalConObj.IncrementPendingCallbacks(); + _pMarsPhysicalConObj.IncrementPendingCallbacks(); - error = SniNativeWrapper.SniReadAsync(_pMarsPhysicalConObj.Handle, ref temp); + error = SniNativeWrapper.SniReadAsync(_pMarsPhysicalConObj.Handle, ref temp); - if (temp != IntPtr.Zero) - { - // Be sure to release packet, otherwise it will be leaked by native. - SniNativeWrapper.SniPacketRelease(temp); - } + if (temp != IntPtr.Zero) + { + // Be sure to release packet, otherwise it will be leaked by native. + SniNativeWrapper.SniPacketRelease(temp); } + Debug.Assert(IntPtr.Zero == temp, "unexpected syncReadPacket without corresponding SNIPacketRelease"); if (TdsEnums.SNI_SUCCESS_IO_PENDING != error) { @@ -4700,87 +4695,68 @@ internal int GetCodePage(SqlCollation collation, TdsParserStateObject stateObj) internal void DrainData(TdsParserStateObject stateObj) { - RuntimeHelpers.PrepareConstrainedRegions(); try { - try + SqlDataReader.SharedState sharedState = stateObj._readerState; + if (sharedState != null && sharedState._dataReady) { - SqlDataReader.SharedState sharedState = stateObj._readerState; - if (sharedState != null && sharedState._dataReady) + var metadata = stateObj._cleanupMetaData; + if (stateObj._partialHeaderBytesRead > 0) { - var metadata = stateObj._cleanupMetaData; - if (stateObj._partialHeaderBytesRead > 0) + TdsOperationStatus result = stateObj.TryProcessHeader(); + if (result != TdsOperationStatus.Done) { - TdsOperationStatus result = stateObj.TryProcessHeader(); - if (result != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } + throw SQL.SynchronousCallMayNotPend(); } - if (0 == sharedState._nextColumnHeaderToRead) + } + if (0 == sharedState._nextColumnHeaderToRead) + { + // i. user called read but didn't fetch anything + TdsOperationStatus result = stateObj.Parser.TrySkipRow(stateObj._cleanupMetaData, stateObj); + if (result != TdsOperationStatus.Done) { - // i. user called read but didn't fetch anything - TdsOperationStatus result = stateObj.Parser.TrySkipRow(stateObj._cleanupMetaData, stateObj); - if (result != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } + throw SQL.SynchronousCallMayNotPend(); } - else + } + else + { + // iia. if we still have bytes left from a partially read column, skip + if (sharedState._nextColumnDataToRead < sharedState._nextColumnHeaderToRead) { - // iia. if we still have bytes left from a partially read column, skip - if (sharedState._nextColumnDataToRead < sharedState._nextColumnHeaderToRead) + if ((sharedState._nextColumnHeaderToRead > 0) && (metadata[sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) { - if ((sharedState._nextColumnHeaderToRead > 0) && (metadata[sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) + if (stateObj._longlen != 0) { - if (stateObj._longlen != 0) - { - TdsOperationStatus result = TrySkipPlpValue(UInt64.MaxValue, stateObj, out _); - if (result != TdsOperationStatus.Done) - { - throw SQL.SynchronousCallMayNotPend(); - } - } - } - - else if (0 < sharedState._columnDataBytesRemaining) - { - TdsOperationStatus result = stateObj.TrySkipLongBytes(sharedState._columnDataBytesRemaining); + TdsOperationStatus result = TrySkipPlpValue(UInt64.MaxValue, stateObj, out _); if (result != TdsOperationStatus.Done) { throw SQL.SynchronousCallMayNotPend(); } } - } - - // Read the remaining values off the wire for this row - if (stateObj.Parser.TrySkipRow(metadata, sharedState._nextColumnHeaderToRead, stateObj) != TdsOperationStatus.Done) + else if (0 < sharedState._columnDataBytesRemaining) { - throw SQL.SynchronousCallMayNotPend(); + TdsOperationStatus result = stateObj.TrySkipLongBytes(sharedState._columnDataBytesRemaining); + if (result != TdsOperationStatus.Done) + { + throw SQL.SynchronousCallMayNotPend(); + } } + + } + + + // Read the remaining values off the wire for this row + if (stateObj.Parser.TrySkipRow(metadata, sharedState._nextColumnHeaderToRead, stateObj) != TdsOperationStatus.Done) + { + throw SQL.SynchronousCallMayNotPend(); } } - Run(RunBehavior.Clean, null, null, null, stateObj); } - catch - { - _connHandler.DoomThisConnection(); - throw; - } - } - catch (OutOfMemoryException) - { - _connHandler.DoomThisConnection(); - throw; - } - catch (StackOverflowException) - { - _connHandler.DoomThisConnection(); - throw; + Run(RunBehavior.Clean, null, null, null, stateObj); } - catch (ThreadAbortException) + catch { _connHandler.DoomThisConnection(); throw; @@ -10416,26 +10392,7 @@ private void FinalizeExecuteRPC(TdsParserStateObject stateObj) private void TdsExecuteRPC_OnFailure(Exception exc, TdsParserStateObject stateObj) { - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - FailureCleanup(stateObj, exc); - } - catch (OutOfMemoryException) - { - _connHandler.DoomThisConnection(); - throw; - } - catch (StackOverflowException) - { - _connHandler.DoomThisConnection(); - throw; - } - catch (ThreadAbortException) - { - _connHandler.DoomThisConnection(); - throw; - } + FailureCleanup(stateObj, exc); } private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, TaskCompletionSource completion, bool releaseConnectionLock) @@ -10446,30 +10403,10 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T if (tsk.Exception != null) { Exception exc = tsk.Exception.InnerException; - - RuntimeHelpers.PrepareConstrainedRegions(); try { FailureCleanup(stateObj, tsk.Exception); } - catch (OutOfMemoryException e) - { - _connHandler.DoomThisConnection(); - completion.SetException(e); - throw; - } - catch (StackOverflowException e) - { - _connHandler.DoomThisConnection(); - completion.SetException(e); - throw; - } - catch (ThreadAbortException e) - { - _connHandler.DoomThisConnection(); - completion.SetException(e); - throw; - } catch (Exception e) { exc = e; @@ -12151,33 +12088,14 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati StripPreamble(buffer, ref offset, ref count); - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - Task task = null; - if (count > 0) - { - _parser.WriteInt(count, _stateObj); // write length of chunk - task = _stateObj.WriteByteArray(buffer, count, offset, canAccumulate: false); - } - - return task ?? Task.CompletedTask; - } - catch (OutOfMemoryException) - { - _parser._connHandler.DoomThisConnection(); - throw; - } - catch (StackOverflowException) - { - _parser._connHandler.DoomThisConnection(); - throw; - } - catch (ThreadAbortException) + Task task = null; + if (count > 0) { - _parser._connHandler.DoomThisConnection(); - throw; + _parser.WriteInt(count, _stateObj); // write length of chunk + task = _stateObj.WriteByteArray(buffer, count, offset, canAccumulate: false); } + + return task ?? Task.CompletedTask; } internal static void ValidateWriteParameters(byte[] buffer, int offset, int count) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs index 6841904b33..758be76457 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs @@ -98,29 +98,10 @@ internal void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey) } } - // @TODO: Consider adopting this pattern for all usages of Run and rename to Run. + // @TODO: Remove. internal bool RunReliably(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) { - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - return Run(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj); - } - catch (OutOfMemoryException) - { - _connHandler.DoomThisConnection(); - throw; - } - catch (StackOverflowException) - { - _connHandler.DoomThisConnection(); - throw; - } - catch (ThreadAbortException) - { - _connHandler.DoomThisConnection(); - throw; - } + return Run(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj); } } } 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 cf54e0340f..a13c5aaa54 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 @@ -173,25 +173,18 @@ internal void Dispose() // here for the callbacks!!! This only applies to async. Should be fixed by async fixes for // AD unload/exit. - // TODO: Make this a BID trace point! - RuntimeHelpers.PrepareConstrainedRegions(); - try - { } - finally + if (packetHandle != null) { - if (packetHandle != null) - { - packetHandle.Dispose(); - } - if (asyncAttnPacket != null) - { - asyncAttnPacket.Dispose(); - } - if (sessionHandle != null) - { - sessionHandle.Dispose(); - DecrementPendingCallbacks(true); // Will dispose of GC handle. - } + packetHandle.Dispose(); + } + if (asyncAttnPacket != null) + { + asyncAttnPacket.Dispose(); + } + if (sessionHandle != null) + { + sessionHandle.Dispose(); + DecrementPendingCallbacks(true); // Will dispose of GC handle. } } @@ -260,7 +253,6 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error) PacketHandle syncReadPacket = default; bool readFromNetwork = true; - RuntimeHelpers.PrepareConstrainedRegions(); bool shouldDecrement = false; try { @@ -371,7 +363,6 @@ public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error) return; } - RuntimeHelpers.PrepareConstrainedRegions(); bool processFinallyBlock = true; try { @@ -654,14 +645,7 @@ private Task SNIWritePacket(PacketHandle packet, out uint sniError, bool canAccu } // Async operation completion may be delayed (success pending). - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - } - finally - { - sniError = WritePacket(packet, sync); - } + sniError = WritePacket(packet, sync); if (sniError == TdsEnums.SNI_SUCCESS_IO_PENDING) { @@ -769,7 +753,6 @@ internal void SendAttention(bool mustTakeWriteLock = false, bool asyncClose = fa PacketHandle attnPacket = CreateAndSetAttentionPacket(); - RuntimeHelpers.PrepareConstrainedRegions(); try { // Dev11 #344723: SqlClient stress test suspends System_Data!Tcp::ReadSync via a call to SqlDataReader::Close diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 807474d98b..3eef048cff 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -186,16 +186,8 @@ internal override void DisposePacketCache() { lock (_writePacketLockObject) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { } - finally - { - _writePacketCache.Dispose(); - // Do not set _writePacketCache to null, just in case a WriteAsyncCallback completes after this point - } + _writePacketCache.Dispose(); + // Do not set _writePacketCache to null, just in case a WriteAsyncCallback completes after this point } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs index f53732940c..d777710eee 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs @@ -37,25 +37,15 @@ internal static DataTable GetDataSources() bool more = true; bool failure = false; IntPtr handle = ADP.s_ptrZero; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif try { long s_timeoutTime = TdsParserStaticMethods.GetTimeoutSeconds(ADP.DefaultCommandTimeout); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { } - finally - { - handle = SniNativeWrapper.SniServerEnumOpen(); - SqlClientEventSource.Log.TryTraceEvent(" {2} returned handle = {3}.", - nameof(SqlDataSourceEnumeratorNativeHelper), - nameof(GetDataSources), - nameof(SniNativeWrapper.SniServerEnumOpen), handle); - } + + handle = SniNativeWrapper.SniServerEnumOpen(); + SqlClientEventSource.Log.TryTraceEvent(" {2} returned handle = {3}.", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SniNativeWrapper.SniServerEnumOpen), handle); if (handle != ADP.s_ptrZero) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs index 37dc781dbc..ea4c46753e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs @@ -158,57 +158,49 @@ internal SNIHandle( string hostNameInCertificate) : base(IntPtr.Zero, true) { -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { } - finally - { - _fSync = fSync; - instanceName = new byte[256]; // Size as specified by netlibs. - // Option ignoreSniOpenTimeout is no longer available - //if (ignoreSniOpenTimeout) - //{ - // // UNDONE: ITEM12001110 (DB Mirroring Reconnect) Old behavior of not truly honoring timeout presevered - // // for non-failover scenarios to avoid breaking changes as part of a QFE. Consider fixing timeout - // // handling in next full release and removing ignoreSniOpenTimeout parameter. - // timeout = Timeout.Infinite; // -1 == native SNIOPEN_TIMEOUT_VALUE / INFINITE - //} - - #if NETFRAMEWORK - int transparentNetworkResolutionStateNo = (int)transparentNetworkResolutionState; - _status = SniNativeWrapper.SniOpenSyncEx( - myInfo, - serverName, - ref base.handle, - ref spn, - instanceName, - flushCache, - fSync, - timeout, - fParallel, - transparentNetworkResolutionStateNo, - totalTimeout, - ipPreference, - cachedDNSInfo, - hostNameInCertificate); - #else - _status = SniNativeWrapper.SniOpenSyncEx( - myInfo, - serverName, - ref base.handle, - ref spn, - instanceName, - flushCache, - fSync, - timeout, - fParallel, - ipPreference, - cachedDNSInfo, - hostNameInCertificate); - #endif - } + _fSync = fSync; + instanceName = new byte[256]; // Size as specified by netlibs. + // Option ignoreSniOpenTimeout is no longer available + //if (ignoreSniOpenTimeout) + //{ + // // UNDONE: ITEM12001110 (DB Mirroring Reconnect) Old behavior of not truly honoring timeout presevered + // // for non-failover scenarios to avoid breaking changes as part of a QFE. Consider fixing timeout + // // handling in next full release and removing ignoreSniOpenTimeout parameter. + // timeout = Timeout.Infinite; // -1 == native SNIOPEN_TIMEOUT_VALUE / INFINITE + //} + + #if NETFRAMEWORK + int transparentNetworkResolutionStateNo = (int)transparentNetworkResolutionState; + _status = SniNativeWrapper.SniOpenSyncEx( + myInfo, + serverName, + ref base.handle, + ref spn, + instanceName, + flushCache, + fSync, + timeout, + fParallel, + transparentNetworkResolutionStateNo, + totalTimeout, + ipPreference, + cachedDNSInfo, + hostNameInCertificate); + #else + _status = SniNativeWrapper.SniOpenSyncEx( + myInfo, + serverName, + ref base.handle, + ref spn, + instanceName, + flushCache, + fSync, + timeout, + fParallel, + ipPreference, + cachedDNSInfo, + hostNameInCertificate); + #endif } // constructs SNI Handle for MARS session 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 2d1810abfb..a1458f0798 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -2844,7 +2844,6 @@ internal void ReadSniSyncOverAsync() bool readFromNetwork = !PartialPacketContainsCompletePacket(); uint error; - RuntimeHelpers.PrepareConstrainedRegions(); bool shouldDecrement = false; try { @@ -3062,7 +3061,6 @@ private bool OnTimeoutCore(int expectedState, int targetState, bool asyncClose = if (!source.Task.IsCompleted) { int pendingCallback = IncrementPendingCallbacks(); - RuntimeHelpers.PrepareConstrainedRegions(); try { // If pendingCallback is at 3, then ReadAsyncCallback hasn't been called yet @@ -3137,7 +3135,6 @@ internal void ReadSni(TaskCompletionSource completion) uint error = 0; bool readFromNetwork = true; - RuntimeHelpers.PrepareConstrainedRegions(); try { Debug.Assert(completion != null, "Async on but null asyncResult passed"); @@ -3173,42 +3170,36 @@ internal void ReadSni(TaskCompletionSource completion) SessionHandle handle = default; - RuntimeHelpers.PrepareConstrainedRegions(); + Interlocked.Increment(ref _readingCount); try - { } - finally { - Interlocked.Increment(ref _readingCount); - try - { - handle = SessionHandle; + handle = SessionHandle; - readFromNetwork = !PartialPacketContainsCompletePacket(); - if (readFromNetwork) + readFromNetwork = !PartialPacketContainsCompletePacket(); + if (readFromNetwork) + { + if (!handle.IsNull) { - if (!handle.IsNull) - { - IncrementPendingCallbacks(); + IncrementPendingCallbacks(); - readPacket = ReadAsync(handle, out error); + readPacket = ReadAsync(handle, out error); - if (!(TdsEnums.SNI_SUCCESS == error || TdsEnums.SNI_SUCCESS_IO_PENDING == error)) - { - DecrementPendingCallbacks(false); // Failure - we won't receive callback! - } + if (!(TdsEnums.SNI_SUCCESS == error || TdsEnums.SNI_SUCCESS_IO_PENDING == error)) + { + DecrementPendingCallbacks(false); // Failure - we won't receive callback! } } - else - { - readPacket = default; - error = TdsEnums.SNI_SUCCESS; - } } - finally + else { - Interlocked.Decrement(ref _readingCount); + readPacket = default; + error = TdsEnums.SNI_SUCCESS; } } + finally + { + Interlocked.Decrement(ref _readingCount); + } if (handle.IsNull) { From 6770634bc92f24d14e82c1a19857c21989564ead Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 29 Jul 2025 13:36:58 -0500 Subject: [PATCH 20/21] Removing the last batches of CER exception stuff --- .../Microsoft/Data/SqlClient/SqlCommand.cs | 40 ------------------- .../Microsoft/Data/SqlClient/SqlCommand.cs | 1 - .../Microsoft/Data/SqlClient/SqlConnection.cs | 1 - .../Data/SqlClient/SqlConnectionHelper.cs | 1 - .../Data/SqlClient/TdsParser.netfx.cs | 35 ---------------- .../SqlClient/TdsParserStateObject.netfx.cs | 1 - .../Interop/Windows/Sni/SniNativeWrapper.cs | 1 - .../src/Microsoft/Data/Common/AdapterUtil.cs | 1 - .../Data/ProviderBase/DbConnectionInternal.cs | 3 -- .../DbConnectionPoolAuthenticationContext.cs | 3 -- .../WaitHandleDbConnectionPool.cs | 4 -- .../SqlClient/Diagnostics/SqlClientMetrics.cs | 1 - .../Microsoft/Data/SqlClient/SqlDependency.cs | 4 +- .../Data/SqlClient/SqlInternalConnection.cs | 25 ------------ .../Data/SqlClient/TdsParserSessionPool.cs | 1 - .../Data/SqlClient/TdsParserStateObject.cs | 3 -- 16 files changed, 2 insertions(+), 123 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 2b009e38f7..c84ade4ba2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -1411,18 +1411,6 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection); _stateObj.ReadSni(completion); } - // Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions, - // trying to call further functions in the catch of either may fail that should be considered on debuging! - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - throw; - } catch (Exception) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -1968,20 +1956,6 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteXmlReader), _activeConnection); _stateObj.ReadSni(completion); } - // Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions, - // trying to call further functions in the catch of either may fail that should be considered on debuging! - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - completion.TrySetException(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - completion.TrySetException(e); - throw; - } catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -2633,20 +2607,6 @@ private void BeginExecuteReaderInternalReadStage(TaskCompletionSource co CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection); _stateObj.ReadSni(completion); } - // Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions, - // trying to call further functions in the catch of either may fail that should be considered on debuging! - catch (System.OutOfMemoryException e) - { - _activeConnection.Abort(e); - completion.TrySetException(e); - throw; - } - catch (System.StackOverflowException e) - { - _activeConnection.Abort(e); - completion.TrySetException(e); - throw; - } catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index fea56991e7..3302d2277f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -307,7 +307,6 @@ internal bool IsActiveConnectionValid(SqlConnection activeConnection) return (_cachedAsyncConnection == activeConnection && _cachedAsyncCloseCount == activeConnection.CloseCount); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] internal void ResetAsyncState() { SqlClientEventSource.Log.TryTraceEvent("CachedAsyncState.ResetAsyncState | API | ObjectId {0}, Client Connection Id {1}, AsyncCommandInProgress={2}", diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 3e7dde1850..50971692f1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -498,7 +498,6 @@ public bool StatisticsEnabled internal bool AsyncCommandInProgress { get => _AsyncCommandInProgress; - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] set => _AsyncCommandInProgress = value; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs index 68c7d24ea2..efd2062c4d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs @@ -170,7 +170,6 @@ internal DbConnectionOptions UserConnectionOptions } // Open->ClosedPreviouslyOpened, and doom the internal connection too... - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] internal void Abort(Exception e) { DbConnectionInternal innerConnection = _innerConnection; // Should not cause memory allocation... diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs index 758be76457..0fba4f5b5c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs @@ -9,41 +9,6 @@ namespace Microsoft.Data.SqlClient { internal sealed partial class TdsParser { - // This is called from a ThreadAbort - ensure that it can be run from a CER Catch - internal void BestEffortCleanup() - { - _state = TdsParserState.Broken; - - var stateObj = _physicalStateObj; - if (stateObj != null) - { - var stateObjHandle = stateObj.Handle; - if (stateObjHandle != null) - { - stateObjHandle.Dispose(); - } - } - - if (_fMARS) - { - var sessionPool = _sessionPool; - if (sessionPool != null) - { - sessionPool.BestEffortCleanup(); - } - - var marsStateObj = _pMarsPhysicalConObj; - if (marsStateObj != null) - { - var marsStateObjHandle = marsStateObj.Handle; - if (marsStateObjHandle != null) - { - marsStateObjHandle.Dispose(); - } - } - } - } - // Retrieve the IP and port number from native SNI for TCP protocol. The IP information is stored temporarily in the // pendingSQLDNSObject but not in the DNS Cache at this point. We only add items to the DNS Cache after we receive the // IsSupported flag as true in the feature ext ack from server. 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 a13c5aaa54..4181012274 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 @@ -136,7 +136,6 @@ internal void CreatePhysicalSNIHandle( internal uint CheckConnection() => SniNativeWrapper.SniCheckConnection(Handle); - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] internal int DecrementPendingCallbacks(bool release) { int remaining = Interlocked.Decrement(ref _pendingCallbacks); diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs index d0d4dfb125..4f90fe3518 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs @@ -403,7 +403,6 @@ internal static void SniPacketSetData( } // Make sure that we clear the security sensitive information - // data->Initialize() is not safe to call under CER if (mustClearBuffer) { for (int i = 0; i < data.Length; ++i) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index 35d232b4da..9ab357884a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -1575,7 +1575,6 @@ internal static string GetComputerNameDnsFullyQualified() return value; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static IntPtr IntPtrOffset(IntPtr pbase, int offset) { if (4 == ADP.s_ptrSize) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 31f207e5ce..e1a86fa0e7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -861,9 +861,6 @@ protected internal void DoNotPoolThisConnection() /// /// Ensure that this connection cannot be put back into the pool. /// - #if NETFRAMEWORK - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - #endif protected internal void DoomThisConnection() { IsConnectionDoomed = true; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolAuthenticationContext.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolAuthenticationContext.cs index 71dbc3c714..2b487b5fb7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolAuthenticationContext.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolAuthenticationContext.cs @@ -102,9 +102,6 @@ internal bool LockToUpdate() /// /// Release the lock which was obtained through LockToUpdate. /// -#if NETFRAMEWORK - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] -#endif internal void ReleaseLockToUpdate() { int oldValue = Interlocked.CompareExchange(ref _isUpdateInProgress, STATUS_UNLOCKED, STATUS_LOCKED); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs index 6568fda7f3..0fc7cfb1a9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs @@ -806,8 +806,6 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio _resError = e; // Make sure the timer starts even if ThreadAbort occurs after setting the ErrorEvent. - - // timer allocation has to be done out of CER block Timer t = new Timer(new TimerCallback(this.ErrorCallback), null, Timeout.Infinite, Timeout.Infinite); bool timerIsNotDisposed; @@ -1619,8 +1617,6 @@ public void ReturnInternalConnection(DbConnectionInternal obj, object owningObje // means, is that we're now responsible for this connection: // it won't get reclaimed if it gets lost. obj.PrePush(owningObject); - - // TODO: Consider using a Cer to ensure that we mark the object for reclaimation in the event something bad happens? } DeactivateObject(obj); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs index f62c65b122..297596853f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs @@ -466,7 +466,6 @@ private void ExceptionEventHandler(object sender, UnhandledExceptionEventArgs e) } } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private void RemovePerformanceCounters() { // ExceptionEventHandler with IsTerminating may be called before diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs index b35a78f33e..0f95e3dcce 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs @@ -613,7 +613,7 @@ internal static bool Start(string connectionString, string queue, bool useDefaul bool appDomainStart = false; try - { // CER to ensure that if Start succeeds we add to hash completing setup. + { // Start using process wide default service/queue & database from connection string. result = s_processDispatcher.StartWithDefault( connectionString, @@ -749,7 +749,7 @@ internal static bool Stop(string connectionString, string queue, bool useDefault bool appDomainStop = false; try - { // CER to ensure that if Stop succeeds we remove from hash completing teardown. + { // Start using process wide default service/queue & database from connection string. result = s_processDispatcher.Stop( connectionString, diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs index 03800a627f..5d39175b70 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs @@ -633,30 +633,5 @@ internal void OnError(SqlException exception, bool breakConnection, Action Date: Fri, 22 Aug 2025 14:52:58 -0500 Subject: [PATCH 21/21] Add comments that indicate where CER exception handling was removed --- .../src/Microsoft/Data/SqlClient/SqlCommand.cs | 3 +++ .../src/Microsoft/Data/SqlClient/SqlConnection.cs | 2 ++ .../Data/SqlClient/SqlInternalConnectionTds.cs | 2 ++ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 4 ++++ .../src/Microsoft/Data/SqlClient/SqlCommand.cs | 14 ++++++++++++++ .../src/Microsoft/Data/SqlClient/SqlConnection.cs | 3 +++ .../Data/SqlClient/SqlInternalConnectionTds.cs | 2 ++ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 4 ++++ .../Microsoft/Data/SqlClient/TdsParser.netfx.cs | 1 + .../ConnectionPool/WaitHandleDbConnectionPool.cs | 1 + .../src/Microsoft/Data/SqlClient/SqlBulkCopy.cs | 2 ++ .../Microsoft/Data/SqlClient/SqlCommandBuilder.cs | 1 + .../src/Microsoft/Data/SqlClient/SqlDataReader.cs | 13 ++++++++++++- .../Data/SqlClient/SqlDelegatedTransaction.cs | 4 ++++ .../Data/SqlClient/SqlInternalConnection.cs | 3 +++ .../src/Microsoft/Data/SqlClient/SqlTransaction.cs | 5 +++++ .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 2 ++ 17 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index c84ade4ba2..236cb8d888 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -1411,6 +1411,7 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection); _stateObj.ReadSni(completion); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -1956,6 +1957,7 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteXmlReader), _activeConnection); _stateObj.ReadSni(completion); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -2607,6 +2609,7 @@ private void BeginExecuteReaderInternalReadStage(TaskCompletionSource co CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection); _stateObj.ReadSni(completion); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 372d0e1ab0..12dafefc53 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1243,6 +1243,7 @@ public override void ChangeDatabase(string database) statistics = SqlStatistics.StartTimer(Statistics); InnerConnection.ChangeDatabase(database); } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { SqlStatistics.StopTimer(statistics); @@ -1334,6 +1335,7 @@ public override void Close() _statistics._closeTimestamp = ADP.TimerCurrent(); } } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception ex) { e = ex; 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 cb9c533cbe..1871fe6087 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 @@ -570,6 +570,7 @@ internal SqlInternalConnectionTds( } } } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { ThreadHasParserLockForClose = false; @@ -2130,6 +2131,7 @@ internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = return true; } return false; + // @TODO: CER Exception Handling was removed here (see GH#3581) } finally { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 4b38f90f96..1309b646e7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -4716,6 +4716,7 @@ internal void DrainData(TdsParserStateObject stateObj) } Run(RunBehavior.Clean, null, null, null, stateObj); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch { _connHandler.DoomThisConnection(); @@ -10213,6 +10214,7 @@ private void FinalizeExecuteRPC(TdsParserStateObject stateObj) private void TdsExecuteRPC_OnFailure(Exception exc, TdsParserStateObject stateObj) { FailureCleanup(stateObj, exc); + // @TODO: CER Exception Handling was removed here (see GH#3581) } private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, TaskCompletionSource completion, bool releaseConnectionLock) @@ -10228,6 +10230,7 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T { FailureCleanup(stateObj, tsk.Exception); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { exc = e; @@ -11923,6 +11926,7 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati } return task ?? Task.CompletedTask; + // @TODO: CER Exception Handling was removed here (see GH#3581) } internal static void ValidateWriteParameters(byte[] buffer, int offset, int count) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 3302d2277f..b3339f9c50 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -515,6 +515,7 @@ private SqlCommand(SqlCommand from) : this() // cleanup Unprepare(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception) { // we do not really care about errors in unprepare (may be the old connection went bad) @@ -952,6 +953,7 @@ public override void Prepare() InternalPrepare(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { processFinallyBlock = ADP.IsCatchableExceptionType(e); @@ -1107,6 +1109,7 @@ public override void Cancel() } } } + // @TODO: CER Exception Handling was removed here (see GH#3581) } } finally @@ -1420,6 +1423,7 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection); _stateObj.ReadSni(completion); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -1693,6 +1697,7 @@ private object InternalEndExecuteNonQuery( Debug.Assert(_stateObj == null, "non-null state object in EndExecuteNonQuery"); return _rowsAffected; + // @TODO: CER Exception Handling was removed here (see GH#3581) } private Task InternalExecuteNonQuery( @@ -1776,6 +1781,7 @@ private Task InternalExecuteNonQuery( } Debug.Assert(isAsync || _stateObj == null, "non-null state object in InternalExecuteNonQuery"); return task; + // @TODO: CER Exception Handling was removed here (see GH#3581) } /// @@ -1954,6 +1960,7 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteXmlReader), _activeConnection); _stateObj.ReadSni(completion); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -2160,6 +2167,7 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) sqlExceptionNumber = e.Number; throw; } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { SqlStatistics.StopTimer(statistics); @@ -2572,6 +2580,7 @@ private void BeginExecuteReaderInternalReadStage(TaskCompletionSource co CachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection); _stateObj.ReadSni(completion); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. @@ -2602,6 +2611,7 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool is SqlDataReader reader = CompleteAsyncExecuteReader(isInternal); Debug.Assert(_stateObj == null, "non-null state object in InternalEndExecuteReader"); return reader; + // @TODO: CER Exception Handling was removed here (see GH#3581) } /// @@ -4017,6 +4027,7 @@ private void PrepareForTransparentEncryption( describeParameterEncryptionDataReader: describeParameterEncryptionDataReader); } } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { if (CachedAsyncState != null) @@ -4827,6 +4838,7 @@ internal SqlDataReader RunExecuteReader( { return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, isAsync, timeout, out task, asyncWrite && isAsync, isRetry: isRetry); } + // @TODO: CER Exception Handling was removed here (see GH#3581) } @@ -5422,6 +5434,7 @@ private void ValidateCommand(bool isAsync, [CallerMemberName] string method = "" // close any non MARS dead readers, if applicable, and then throw if still busy. // Throw if we have a live reader on this command _activeConnection.ValidateConnectionForExecute(method, this); + // @TODO: CER Exception Handling was removed here (see GH#3581) // Check to see if the currently set transaction has completed. If so, // null out our local reference. @@ -5515,6 +5528,7 @@ private void GetStateObject(TdsParser parser = null) private void ReliablePutStateObject() { PutStateObject(); + // @TODO: CER Exception Handling was removed here (see GH#3581) } private void PutStateObject() diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 50971692f1..67419ac7ab 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1250,6 +1250,7 @@ public override void ChangeDatabase(string database) statistics = SqlStatistics.StartTimer(Statistics); InnerConnection.ChangeDatabase(database); } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { SqlStatistics.StopTimer(statistics); @@ -1323,6 +1324,7 @@ public override void Close() _statistics._closeTimestamp = ADP.TimerCurrent(); } } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { SqlStatistics.StopTimer(statistics); @@ -1890,6 +1892,7 @@ private bool TryOpenInner(TaskCompletionSource retry) tdsInnerConnection.Parser.Statistics = null; _statistics = null; // in case of previous Open/Close/reset_CollectStats sequence } + // @TODO: CER Exception Handling was removed here (see GH#3581) return true; } 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 e57e65648b..2434582205 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 @@ -580,6 +580,7 @@ internal SqlInternalConnectionTds( } } } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { ThreadHasParserLockForClose = false; @@ -2183,6 +2184,7 @@ internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = return true; } return false; + // @TODO: CER Exception Handling was removed here (see GH#3581) } finally { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index e0cf50cc76..756f2f044a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -4750,6 +4750,7 @@ internal void DrainData(TdsParserStateObject stateObj) _connHandler.DoomThisConnection(); throw; } + // @TODO: CER Exception Handling was removed here (see GH#3581) } @@ -10382,6 +10383,7 @@ private void FinalizeExecuteRPC(TdsParserStateObject stateObj) private void TdsExecuteRPC_OnFailure(Exception exc, TdsParserStateObject stateObj) { FailureCleanup(stateObj, exc); + // @TODO: CER Exception Handling was removed here (see GH#3581) } private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, TaskCompletionSource completion, bool releaseConnectionLock) @@ -10396,6 +10398,7 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T { FailureCleanup(stateObj, tsk.Exception); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { exc = e; @@ -12085,6 +12088,7 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati } return task ?? Task.CompletedTask; + // @TODO: CER Exception Handling was removed here (see GH#3581) } internal static void ValidateWriteParameters(byte[] buffer, int offset, int count) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs index 0fba4f5b5c..d7957767fa 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs @@ -67,6 +67,7 @@ internal void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey) internal bool RunReliably(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) { return Run(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj); + // @TODO: CER Exception Handling was removed here (see GH#3581) } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs index 0fc7cfb1a9..4cef28fd78 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs @@ -1072,6 +1072,7 @@ private void WaitForPendingOpen() next.UserOptions, out connection); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { caughtException = e; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 11b3cd980f..649087f32b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -2101,6 +2101,7 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok } return null; } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { _columnMappings.ReadOnly = false; @@ -2777,6 +2778,7 @@ private void CopyBatchesAsyncContinuedOnError(bool cleanupParser) { CleanUpStateObject(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) AbortTransaction(); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs index 88532e8321..9e2a14b3ab 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandBuilder.cs @@ -255,6 +255,7 @@ public static void DeriveParameters(SqlCommand command) throw ADP.ArgumentNull(nameof(command)); } + // @TODO: CER Exception Handling was removed here (see GH#3581) command.DeriveParameters(); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 877781d70e..5e37fc1a47 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -263,12 +263,13 @@ internal _SqlMetaDataSet MetaData { throw SQL.PendingBeginXXXExists(); } - + Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); if (TryConsumeMetaData() != TdsOperationStatus.Done) { throw SQL.SynchronousCallMayNotPend(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) } return _metaData; @@ -846,6 +847,7 @@ private void CleanPartialReadReliable() TdsOperationStatus result = TryCleanPartialRead(); Debug.Assert(result == TdsOperationStatus.Done, "Should not pend on sync call"); Debug.Assert(!_sharedState._dataReady, "_dataReady should be cleared"); + // @TODO: CER Exception Handling was removed here (see GH#3581) } /// @@ -1021,6 +1023,7 @@ private TdsOperationStatus TryCloseInternal(bool closeReader) RestoreServerSettings(parser, stateObj); return TdsOperationStatus.Done; } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { if (closeReader) @@ -1067,6 +1070,7 @@ private TdsOperationStatus TryCloseInternal(bool closeReader) } } } + // @TODO: CER Exception Handling was removed here (see GH#3581) // DO NOT USE stateObj after this point - it has been returned to the TdsParser's session pool and potentially handed out to another thread @@ -1878,6 +1882,7 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf remaining = cbytes; return TdsOperationStatus.Done; + // @TODO: CER Exception Handling was removed here (see GH#3581) } internal int GetBytesInternalSequential(int i, byte[] buffer, int index, int length, long? timeoutMilliseconds = null) @@ -1970,6 +1975,7 @@ internal TdsOperationStatus TryGetBytesInternalSequential(int i, byte[] buffer, return result; } } + // @TODO: CER Exception Handling was removed here (see GH#3581) } /// @@ -2328,6 +2334,7 @@ private long GetCharsFromPlpData(int i, long dataIndex, char[] buffer, int buffe _columnDataCharsRead += cch; _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj); return cch; + // @TODO: CER Exception Handling was removed here (see GH#3581) } internal long GetStreamingXmlChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) @@ -3621,6 +3628,7 @@ private TdsOperationStatus TryNextResult(out bool more) more = success; return TdsOperationStatus.Done; } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { SqlStatistics.StopTimer(statistics); @@ -3814,6 +3822,7 @@ private TdsOperationStatus TryReadInternal(bool setTimeout, out bool more) return TdsOperationStatus.Done; } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { SqlStatistics.StopTimer(statistics); @@ -3855,6 +3864,7 @@ private TdsOperationStatus TryReadColumn(int i, bool setTimeout, bool allowParti } Debug.Assert(_data[i] != null, " data buffer is null?"); + // @TODO: CER Exception Handling was removed here (see GH#3581) return TdsOperationStatus.Done; } @@ -3899,6 +3909,7 @@ private TdsOperationStatus TryReadColumnHeader(int i) } return TryReadColumnInternal(i, readHeaderOnly: true); + // @TODO: CER Exception Handling was removed here (see GH#3581) } internal TdsOperationStatus TryReadColumnInternal(int i, bool readHeaderOnly = false, bool forStreaming = false) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index a3b8fde8a1..330f60f9b9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -102,6 +102,7 @@ public void Initialize() } _active = true; + // @TODO: CER Exception Handling was removed here (see GH#3581) } internal bool IsActive @@ -173,6 +174,7 @@ public byte[] Promote() connection.DoomThisConnection(); } } + // @TODO: CER Exception Handling was removed here (see GH#3581) //Throw exception only if Transaction is still active and not yet aborted. if (promoteException != null) @@ -264,6 +266,7 @@ public void Rollback(SinglePhaseEnlistment enlistment) // that the transaction is aborted, because it will be eventually. connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); enlistment.Aborted(); + // @TODO: CER Exception Handling was removed here (see GH#3581) } else { @@ -366,6 +369,7 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) enlistment.Committed(); } } + // @TODO: CER Exception Handling was removed here (see GH#3581) } else { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs index 5d39175b70..feb4ad8061 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs @@ -241,6 +241,7 @@ virtual internal SqlTransaction BeginSqlTransaction(System.Data.IsolationLevel i transaction.InternalTransaction.RestoreBrokenConnection = false; return transaction; } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { SqlStatistics.StopTimer(statistics); @@ -292,6 +293,7 @@ override protected void Deactivate() // Invoke subclass-specific deactivation logic InternalDeactivate(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { if (!ADP.IsCatchableExceptionType(e)) @@ -577,6 +579,7 @@ override public void EnlistTransaction(Transaction transaction) // behavior matches OLEDB and ODBC. Enlist(transaction); + // @TODO: CER Exception Handling was removed here (see GH#3581) } abstract internal void ExecuteTransaction(TransactionRequest transactionRequest, string name, System.Data.IsolationLevel iso, SqlInternalTransaction internalTransaction, bool isDelegateControlRequest); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 58a371ec7f..d6302dab20 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -115,6 +115,7 @@ public override void Commit() InternalTransaction.Commit(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (SqlException ex) { #if NET @@ -154,6 +155,7 @@ protected override void Dispose(bool disposing) { InternalTransaction.Dispose(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) } base.Dispose(disposing); @@ -200,6 +202,7 @@ public override void Rollback() _isFromApi = true; InternalTransaction.Rollback(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) #if NET catch (Exception ex) { @@ -253,6 +256,7 @@ public void Rollback(string transactionName) _isFromApi = true; InternalTransaction.Rollback(transactionName); } + // @TODO: CER Exception Handling was removed here (see GH#3581) #if NET catch (Exception ex) { @@ -290,6 +294,7 @@ public void Save(string savePointName) InternalTransaction.Save(savePointName); } + // @TODO: CER Exception Handling was removed here (see GH#3581) finally { SqlStatistics.StopTimer(statistics); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs index 454505961c..3a460fc1d8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -184,6 +184,7 @@ internal static void ContinueTask(Task task, { onSuccess(); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { completion.SetException(e); @@ -276,6 +277,7 @@ internal static void ContinueTaskWithState(Task task, { onSuccess(state2); } + // @TODO: CER Exception Handling was removed here (see GH#3581) catch (Exception e) { completion.SetException(e);