public void Rollback(string transactionName) { SqlConnection.ExecutePermission.Demand(); // MDAC 81476 ZombieCheck(); SqlStatistics statistics = null; IntPtr hscp; Bid.ScopeEnter(out hscp, "<sc.SqlTransaction.Rollback|API> %d# transactionName='%ls'", ObjectID, transactionName); TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); _isFromAPI = true; _internalTransaction.Rollback(transactionName); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException e) { _connection.Abort(e); throw; } catch (System.StackOverflowException e) { _connection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { _connection.Abort(e); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } finally { _isFromAPI = false; SqlStatistics.StopTimer(statistics); Bid.ScopeLeave(ref hscp); } }
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml' path='docs/members[@name="SqlCommandBuilder"]/DeriveParameters/*'/> public static void DeriveParameters(SqlCommand command) { #if NETFRAMEWORK SqlConnection.ExecutePermission.Demand(); #endif if (null == command) { throw ADP.ArgumentNull(nameof(command)); } #if NETFRAMEWORK TdsParser bestEffortCleanupTarget = null; #endif RuntimeHelpers.PrepareConstrainedRegions(); try { #if NETFRAMEWORK #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif // DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(command.Connection); #endif // NETFRAMEWORK command.DeriveParameters(); #if NETFRAMEWORK } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif // DEBUG #endif // NETFRAMEWORK } 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; } }
// Check for connection validity private SqlInternalConnection GetValidConnection() { SqlInternalConnection connection = _connection; if (null == connection && _atomicTransaction.TransactionInformation.Status != SysTx.TransactionStatus.Aborted) { throw ADP.ObjectDisposed(this); } return(connection); }
// Check for connection validity private SqlInternalConnection GetValidConnection() { SqlInternalConnection connection = _connection; if (null == connection) { throw ADP.ObjectDisposed(this); } return(connection); }
static public void DeriveParameters(SqlCommand command) // MDAC 65927\ { SqlConnection.ExecutePermission.Demand(); if (null == command) { throw ADP.ArgumentNull("command"); } TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(command.Connection); command.DeriveParameters(); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif } catch (System.OutOfMemoryException e) { if (null != command && null != command.Connection) { command.Connection.Abort(e); } throw; } catch (System.StackOverflowException e) { if (null != command && null != command.Connection) { command.Connection.Abort(e); } throw; } catch (System.Threading.ThreadAbortException e) { if (null != command && null != command.Connection) { command.Connection.Abort(e); } SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } }
internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) { _innerConnection = innerConnection; _transactionType = type; if (null != outerTransaction) { _parent = new WeakReference(outerTransaction); } _transactionId = transactionId; RestoreBrokenConnection = false; ConnectionHasBeenRestored = false; }
/// <include file='..\..\..\..\..\..\..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml' path='docs/members[@name="SqlTransaction"]/DisposeDisposing/*' /> protected override void Dispose(bool disposing) { if (disposing) { TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); if (!IsZombied && !IsYukonPartialZombie) { _internalTransaction.Dispose(); } } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException e) { _connection.Abort(e); throw; } catch (System.StackOverflowException e) { _connection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { _connection.Abort(e); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } } base.Dispose(disposing); }
//////////////////////////////////////////////////////////////////////////////////////// // INTERNAL METHODS //////////////////////////////////////////////////////////////////////////////////////// internal void Zombie() { // For Yukon, we have to defer "zombification" until // we get past the users' next rollback, else we'll // throw an exception there that is a breaking change. // Of course, if the connection is already closed, // then we're free to zombify... SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); if (internalConnection == null || _isFromAPI) { _internalTransaction = null; // pre-yukon zombification } }
internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) { SqlClientEventSource.Log.TryPoolerTraceEvent("SqlInternalTransaction.ctor | RES | CPOOL | Object Id {0}, Created for connection {1}, outer transaction {2}, Type {3}", ObjectID, innerConnection.ObjectID, outerTransaction?.ObjectID, (int)type); _innerConnection = innerConnection; _transactionType = type; if (null != outerTransaction) { _parent = new WeakReference <SqlTransaction>(outerTransaction); } _transactionId = transactionId; RestoreBrokenConnection = false; ConnectionHasBeenRestored = false; }
public void Initialize() { // if we get here, then we know for certain that we're the delegated // transaction. 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); RuntimeHelpers.PrepareConstrainedRegions(); 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(); } _internalTransaction = new SqlInternalTransaction(connection, TransactionType.Delegated, null); connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Begin, null, _isolationLevel, _internalTransaction, true); // Handle case where ExecuteTran didn't produce a new transaction, but also didn't throw. if (null == connection.CurrentTransaction) { connection.DoomThisConnection(); throw ADP.InternalError(ADP.InternalErrorCode.UnknownTransactionFailure); } _active = true; } 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; } }
internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con, IsolationLevel iso, SqlInternalTransaction internalTransaction) { _isolationLevel = iso; _connection = con; if (internalTransaction == null) { _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); } else { Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!"); _internalTransaction = internalTransaction; _internalTransaction.InitParent(this); } }
// Event notification that transaction ended. This comes from the subscription to the Transaction's // ended event via the internal connection. If it occurs without a prior Rollback or SinglePhaseCommit call, // it indicates the transaction was ended externally (generally that one of the DTC participants aborted // the transaction). internal void TransactionEnded(Transaction transaction) { SqlInternalConnection connection = _connection; if (connection != null) { lock (connection) { if (_atomicTransaction.Equals(transaction)) { // No need to validate active on connection, this operation can be called on completed transactions _active = false; _connection = null; } } } }
//////////////////////////////////////////////////////////////////////////////////////// // INTERNAL METHODS //////////////////////////////////////////////////////////////////////////////////////// internal void Zombie() { // SQLBUDT #402544 For Yukon, we have to defer "zombification" until // we get past the users' next rollback, else we'll // throw an exception there that is a breaking change. // Of course, if the connection is aready closed, // then we're free to zombify... SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); if (null != internalConnection && internalConnection.IsYukonOrNewer && !_isFromAPI) { SqlClientEventSource.Log.AdvanceTrace("<sc.SqlTransaction.Zombie|ADV> {0}# yukon deferred zombie", ObjectID); } else { _internalTransaction = null; // pre-yukon zombification } }
// Event notification that transaction ended. This comes from the subscription to the Transaction's // ended event via the internal connection. If it occurs without a prior Rollback or SinglePhaseCommit call, // it indicates the transaction was ended externally (generally that one the the DTC participants aborted // the transaction). internal void TransactionEnded(SysTx.Transaction transaction) { SqlInternalConnection connection = _connection; if (connection != null) { Bid.Trace("<sc.SqlDelegatedTransaction.TransactionEnded|RES|CPOOL> %d#, Connection %d#, transaction completed externally.\n", ObjectID, connection.ObjectID); lock (connection) { if (_atomicTransaction.Equals(transaction)) { // No need to validate active on connection, this operation can be called on completed transactions _active = false; _connection = null; } } } }
//////////////////////////////////////////////////////////////////////////////////////// // INTERNAL METHODS //////////////////////////////////////////////////////////////////////////////////////// internal void Zombie() { // For 2005, we have to defer "zombification" until // we get past the users' next rollback, else we'll // throw an exception there that is a breaking change. // Of course, if the connection is already closed, // then we're free to zombify... SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); if (null != internalConnection && !_isFromAPI) { SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Zombie | ADV | Object Id {0} 2005 deferred zombie", ObjectID); } else { _internalTransaction = null; // pre-2005 zombification } }
// Event notification that transaction ended. This comes from the subscription to the Transaction's // ended event via the internal connection. If it occurs without a prior Rollback or SinglePhaseCommit call, // it indicates the transaction was ended externally (generally that one of the DTC participants aborted // the transaction). internal void TransactionEnded(Transaction transaction) { SqlInternalConnection connection = _connection; if (connection != null) { SqlClientEventSource.Log.TraceEvent("<sc.SqlDelegatedTransaction.TransactionEnded|RES|CPOOL> {0}, Connection {1}, transaction completed externally.", ObjectID, connection.ObjectID); lock (connection) { if (_atomicTransaction.Equals(transaction)) { // No need to validate active on connection, this operation can be called on completed transactions _active = false; _connection = null; } } } }
internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) { Bid.PoolerTrace("<sc.SqlInternalTransaction.ctor|RES|CPOOL> %d#, Created for connection %d#, outer transaction %d#, Type %d\n", ObjectID, innerConnection.ObjectID, (null != outerTransaction) ? outerTransaction.ObjectID : -1, (int)type); _innerConnection = innerConnection; _transactionType = type; if (null != outerTransaction) { _parent = new WeakReference(outerTransaction); } _transactionId = transactionId; RestoreBrokenConnection = false; ConnectionHasBeenRestored = false; }
// Dooms connection and throws and error if not a valid, active, delegated transaction for the given // connection. Designed to be called AFTER a lock is placed on the connection, otherwise a normal return // may not be trusted. private void ValidateActiveOnConnection(SqlInternalConnection connection) { bool valid = _active && (connection == _connection) && (connection.DelegatedTransaction == this); if (!valid) { // Invalid indicates something BAAAD happened (Commit after TransactionEnded, for instance) // Doom anything remotely involved. if (null != connection) { connection.DoomThisConnection(); } if (connection != _connection && null != _connection) { _connection.DoomThisConnection(); } throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); //TODO: Create a new code } }
private bool _active; // Is the transaction active? internal SqlDelegatedTransaction(SqlInternalConnection connection, SysTx.Transaction tx) { Debug.Assert(null != connection, "null connection?"); _connection = connection; _atomicTransaction = tx; _active = false; SysTx.IsolationLevel systxIsolationLevel = tx.IsolationLevel; // We need to map the System.Transactions IsolationLevel to the one // that System.Data uses and communicates to SqlServer. We could // arguably do that in Initialize when the transaction is delegated, // however it is better to do this before we actually begin the process // of delegation, in case System.Transactions adds another isolation // level we don't know about -- we can throw the exception at a better // place. switch (systxIsolationLevel) { case SysTx.IsolationLevel.ReadCommitted: _isolationLevel = IsolationLevel.ReadCommitted; break; case SysTx.IsolationLevel.ReadUncommitted: _isolationLevel = IsolationLevel.ReadUncommitted; break; case SysTx.IsolationLevel.RepeatableRead: _isolationLevel = IsolationLevel.RepeatableRead; break; case SysTx.IsolationLevel.Serializable: _isolationLevel = IsolationLevel.Serializable; break; case SysTx.IsolationLevel.Snapshot: _isolationLevel = IsolationLevel.Snapshot; break; default: throw SQL.UnknownSysTxIsolationLevel(systxIsolationLevel); } }
// Event notification that transaction ended. This comes from the subscription to the Transaction's // ended event via the internal connection. If it occurs without a prior Rollback or SinglePhaseCommit call, // it indicates the transaction was ended externally (generally that one of the DTC participants aborted // the transaction). internal void TransactionEnded(Transaction transaction) { SqlInternalConnection connection = _connection; if (connection != null) { SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.TransactionEnded | RES | CPOOL | Object Id {0}, Connection Id {1}, transaction completed externally.", ObjectID, connection?._objectID); lock (connection) { if (_atomicTransaction.Equals(transaction)) { // No need to validate active on connection, this operation can be called on completed transactions _active = false; _connection = null; } // Safest approach is to doom this connection, whose transaction has been aborted externally. // If we want to avoid dooming the connection for performance, state needs to be properly restored. (future TODO) connection.DoomThisConnection(); } } }
internal void FailoverCheck(SqlInternalConnection connection, bool actualUseFailoverPartner, SqlConnectionString userConnectionOptions, string actualFailoverPartner) { if (UseFailoverPartner != actualUseFailoverPartner) { base.PoolGroup.Clear(); _useFailoverPartner = actualUseFailoverPartner; } // Only construct a new permission set when we're connecting to the // primary data source, not the failover partner. if (!_useFailoverPartner && _failoverPartner != actualFailoverPartner) { // NOTE: we optimistically generate the permission set to keep // lock short, but we only do this when we get a new // failover partner. lock (this) { if (_failoverPartner != actualFailoverPartner) { _failoverPartner = actualFailoverPartner; } } } }
override protected void Deactivate() { SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnection.Deactivate|ADV> {0} deactivating", ObjectID); TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection); SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection; if (null != referenceCollection) { referenceCollection.Deactivate(); } // Invoke subclass-specific deactivation logic InternalDeactivate(); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException) { DoomThisConnection(); throw; } catch (System.StackOverflowException) { DoomThisConnection(); throw; } catch (System.Threading.ThreadAbortException) { DoomThisConnection(); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } catch (Exception e) { // UNDONE - should not be catching all exceptions!!! if (!ADP.IsCatchableExceptionType(e)) { throw; } // if an exception occurred, the inner connection will be // marked as unusable and destroyed upon returning to the // pool DoomThisConnection(); ADP.TraceExceptionWithoutRethrow(e); } }
virtual internal SqlTransaction BeginSqlTransaction(IsolationLevel iso, string transactionName, bool shouldReconnect) { SqlStatistics statistics = null; TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection); statistics = SqlStatistics.StartTimer(Connection.Statistics); SqlConnection.ExecutePermission.Demand(); // MDAC 81476 ValidateConnectionForExecute(null); if (HasLocalTransactionFromAPI) { throw ADP.ParallelTransactionsNotSupported(Connection); } if (iso == IsolationLevel.Unspecified) { iso = IsolationLevel.ReadCommitted; // Default to ReadCommitted if unspecified. } SqlTransaction transaction = new SqlTransaction(this, Connection, iso, AvailableInternalTransaction); transaction.InternalTransaction.RestoreBrokenConnection = shouldReconnect; ExecuteTransaction(TransactionRequest.Begin, transactionName, iso, transaction.InternalTransaction, false); transaction.InternalTransaction.RestoreBrokenConnection = false; return(transaction); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException e) { Connection.Abort(e); throw; } catch (System.StackOverflowException e) { Connection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { Connection.Abort(e); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } finally { SqlStatistics.StopTimer(statistics); } }
override public void EnlistTransaction(SysTx.Transaction transaction) { SqlConnection.VerifyExecutePermission(); ValidateConnectionForExecute(null); // If a connection has a local transaction outstanding and you try // to enlist in a DTC transaction, SQL Server will rollback the // local transaction and then do the enlist (7.0 and 2000). So, if // the user tries to do this, throw. if (HasLocalTransaction) { throw ADP.LocalTransactionPresent(); } if (null != transaction && transaction.Equals(EnlistedTransaction)) { // No-op if this is the current transaction return; } // If a connection is already enlisted in a DTC transaction and you // try to enlist in another one, in 7.0 the existing DTC transaction // would roll back and then the connection would enlist in the new // one. In SQL 2000 & 2005, when you enlist in a DTC transaction // while the connection is already enlisted in a DTC transaction, // the connection simply switches enlistments. Regardless, simply // enlist in the user specified distributed transaction. This // behavior matches OLEDB and ODBC. TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection); Enlist(transaction); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException e) { Connection.Abort(e); throw; } catch (System.StackOverflowException e) { Connection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { Connection.Abort(e); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } }
/// <include file='..\..\..\..\..\..\..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml' path='docs/members[@name="SqlTransaction"]/Rollback2/*' /> override public void Rollback() { if (IsYukonPartialZombie) { // Put something in the trace in case a customer has an issue SqlClientEventSource.Log.AdvanceTrace("<sc.SqlTransaction.Rollback|ADV> {0}# partial zombie no rollback required", ObjectID); _internalTransaction = null; // yukon zombification } else { ZombieCheck(); SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent("<sc.SqlTransaction.Rollback|API> {0}#", ObjectID); SqlClientEventSource.Log.CorrelationTraceEvent("<sc.SqlTransaction.Rollback|API|Correlation> ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); _isFromAPI = true; _internalTransaction.Rollback(); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException e) { _connection.Abort(e); throw; } catch (System.StackOverflowException e) { _connection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { _connection.Abort(e); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } finally { _isFromAPI = false; SqlStatistics.StopTimer(statistics); SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } }
//////////////////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS //////////////////////////////////////////////////////////////////////////////////////// /// <include file='..\..\..\..\..\..\..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml' path='docs/members[@name="SqlTransaction"]/Commit/*' /> override public void Commit() { SqlConnection.ExecutePermission.Demand(); // MDAC 81476 ZombieCheck(); SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent("<sc.SqlTransaction.Commit|API> {0}#", ObjectID); SqlClientEventSource.Log.CorrelationTraceEvent("<sc.SqlTransaction.Commit|API|Correlation> ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); _isFromAPI = true; _internalTransaction.Commit(); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException e) { _connection.Abort(e); throw; } catch (System.StackOverflowException e) { _connection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { _connection.Abort(e); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } catch (SqlException e) { // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, // this connection may not be in reusable state. // We will abort this connection and make sure it does not go back to the pool. var innerException = e.InnerException as Win32Exception; if (innerException != null && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) { _connection.Abort(e); } throw; } finally { _isFromAPI = false; SqlStatistics.StopTimer(statistics); SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } }
public void Initialize() { // if we get here, then we know for certain that we're the delegated // transaction. SqlInternalConnection connection = _connection; SqlConnection usersConnection = connection.Connection; Bid.Trace("<sc.SqlDelegatedTransaction.Initialize|RES|CPOOL> %d#, Connection %d#, delegating transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG if (connection.IsEnlistedInTransaction) { // defect first Bid.Trace("<sc.SqlDelegatedTransaction.Initialize|RES|CPOOL> %d#, Connection %d#, was enlisted, now defecting.\n", ObjectID, connection.ObjectID); connection.EnlistNull(); } _internalTransaction = new SqlInternalTransaction(connection, TransactionType.Delegated, null); connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Begin, null, _isolationLevel, _internalTransaction, true); // Handle case where ExecuteTran didn't produce a new transaction, but also didn't throw. if (null == connection.CurrentTransaction) { connection.DoomThisConnection(); throw ADP.InternalError(ADP.InternalErrorCode.UnknownTransactionFailure); } _active = true; } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } 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; } }
public Byte[] Promote() { // Operations that might be affected by multi-threaded use MUST be done inside the lock. // Don't read values off of the connection outside the lock unless it doesn't really matter // from an operational standpoint (i.e. logging connection's ObjectID should be fine, // but the PromotedDTCToken can change over calls. so that must be protected). SqlInternalConnection connection = GetValidConnection(); Exception promoteException; byte[] returnValue = null; SqlConnection usersConnection = connection.Connection; Bid.Trace("<sc.SqlDelegatedTransaction.Promote|RES|CPOOL> %d#, Connection %d#, promoting transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG lock (connection) { try { // 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, 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) { if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) { throw SQL.UnsupportedSysTxForGlobalTransactions(); } if (!_connection.IsGlobalTransactionsEnabledForServer) { throw SQL.GlobalTransactionsNotEnabled(); } SysTxForGlobalTransactions.SetDistributedTransactionIdentifier.Invoke(_atomicTransaction, new object[] { this, GetGlobalTxnIdentifierFromToken() }); } promoteException = null; } catch (SqlException e) { promoteException = 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) { promoteException = e; ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } 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; } if (promoteException != null) { throw SQL.PromotionFailed(promoteException); } return(returnValue); }
// Called by transaction to initiate abort sequence public void Rollback(SysTx.SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); SqlInternalConnection connection = GetValidConnection(); SqlConnection usersConnection = connection.Connection; Bid.Trace("<sc.SqlDelegatedTransaction.Rollback|RES|CPOOL> %d#, Connection %d#, aborting transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG lock (connection) { 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 // 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, IsolationLevel.Unspecified, _internalTransaction, true); } } 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(); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } 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; } }
// Called by the transaction to initiate commit sequence public void SinglePhaseCommit(SysTx.SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); SqlInternalConnection connection = GetValidConnection(); SqlConnection usersConnection = connection.Connection; Bid.Trace("<sc.SqlDelegatedTransaction.SinglePhaseCommit|RES|CPOOL> %d#, Connection %d#, committing transaction.\n", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG // If the connection is dooomed, we can be certain that the // transaction will eventually be rolled back, and we shouldn't // attempt to commit it. if (connection.IsConnectionDoomed) { lock (connection) { _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; } enlistment.Aborted(SQL.ConnectionDoomed()); } else { Exception commitException; lock (connection) { 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 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, IsolationLevel.Unspecified, _internalTransaction, true); commitException = null; } catch (SqlException e) { commitException = 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(); } } if (commitException != null) { // connection.ExecuteTransaction failed with exception if (_internalTransaction.IsCommitted) { // Even though we got an exception, the transaction // was committed by the server. enlistment.Committed(); } else if (_internalTransaction.IsAborted) { // The transaction was aborted, report that to // SysTx. enlistment.Aborted(commitException); } else { // The transaction is still active, we cannot // know the state of the transaction. enlistment.InDoubt(commitException); } // We eat the exception. This is called on the SysTx // thread, not the applications thread. If we don't // eat the exception an UnhandledException will occur, // causing the process to FailFast. } connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); if (commitException == null) { // connection.ExecuteTransaction succeeded enlistment.Committed(); } } } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } 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; } }