abstract public void EnlistTransaction(SysTx.Transaction transaction);
// Cleanup connection's transaction-specific structures (currently used by Delegated transaction). // This is a separate method because cleanup can be triggered in multiple ways for a delegated // transaction. virtual protected void CleanupTransactionOnCompletion(SysTx.Transaction transaction) { }
override public void EnlistTransaction(SysTx.Transaction transaction) { // MDAC 78997 OleDbConnection.VerifyExecutePermission(); OleDbConnection outerConnection = Connection; if (null != LocalTransaction) { throw ADP.LocalTransactionPresent(); } EnlistTransactionInternal(transaction); }
internal void ActivateConnection(SysTx.Transaction transaction) { // Internal method called from the connection pooler so we don't expose // the Activate method publicly. Bid.PoolerTrace("<prov.DbConnectionInternal.ActivateConnection|RES|INFO|CPOOL> %d#, Activating\n", ObjectID); #if DEBUG int activateCount = Interlocked.Increment(ref _activateCount); Debug.Assert(1 == activateCount, "activated multiple times?"); #endif // DEBUG Activate(transaction); PerformanceCounters.NumberOfActiveConnections.Increment(); }
override public void EnlistTransaction(SysTx.Transaction transaction) { CONNECTIONOBJECTNAME.ExecutePermission.Demand(); Bid.Trace( "<prov.DbConnectionHelper.EnlistTransaction|RES|TRAN> %d#, Connection enlisting in a transaction.\n", ObjectID); // If we're currently enlisted in a transaction and we were called // on the EnlistTransaction method (Whidbey) we're not allowed to // enlist in a different transaction. DbConnectionInternal innerConnection = InnerConnection; // NOTE: since transaction enlistment involves round trips to the // server, we don't want to lock here, we'll handle the race conditions // elsewhere. SysTx.Transaction enlistedTransaction = innerConnection.EnlistedTransaction; if (enlistedTransaction != null) { // Allow calling enlist if already enlisted (no-op) if (enlistedTransaction.Equals(transaction)) { return; } // Allow enlisting in a different transaction if the enlisted transaction has completed. if (enlistedTransaction.TransactionInformation.Status == SysTx.TransactionStatus.Active) { throw ADP.TransactionPresent(); } } RepairInnerConnection(); InnerConnection.EnlistTransaction(transaction); // NOTE: If this outer connection were to be GC'd while we're // enlisting, the pooler would attempt to reclaim the inner connection // while we're attempting to enlist; not sure how likely that is but // we should consider a GC.KeepAlive(this) here. GC.KeepAlive(this); }
override public void EnlistTransaction(SysTx.Transaction transaction) { OuterConnection.Open_EnlistTransaction(transaction); }
static private byte[] GetTransactionCookie(SysTx.Transaction transaction, byte[] whereAbouts) { byte[] transactionCookie = null; if (null != transaction) { transactionCookie = SysTx.TransactionInterop.GetExportCookie(transaction, whereAbouts); } return transactionCookie; }
void TransactionCompletedEvent(object sender, SysTx.TransactionEventArgs e) { SysTx.Transaction transaction = e.Transaction; Bid.Trace("<prov.DbConnectionInternal.TransactionCompletedEvent|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", ObjectID, _pooledCount); CleanupTransactionOnCompletion(transaction); CleanupConnectionOnTransactionCompletion(transaction); }
private void EnlistNonNull(SysTx.Transaction tx) { Debug.Assert(null != tx, "null transaction?"); if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, transaction %d#.\n", base.ObjectID, tx.GetHashCode()); } bool hasDelegatedTransaction = false; if (IsYukonOrNewer) { if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, attempting to delegate\n", base.ObjectID); } // Promotable transactions are only supported on Yukon // servers or newer. SqlDelegatedTransaction delegatedTransaction = new SqlDelegatedTransaction(this, tx); try { // NOTE: System.Transactions claims to resolve all // potential race conditions between multiple delegate // requests of the same transaction to different // connections in their code, such that only one // attempt to delegate will succeed. // NOTE: PromotableSinglePhaseEnlist will eventually // make a round trip to the server; doing this inside // a lock is not the best choice. We presume that you // aren't trying to enlist concurrently on two threads // and leave it at that -- We don't claim any thread // safety with regard to multiple concurrent requests // to enlist the same connection in different // transactions, which is good, because we don't have // it anyway. // PromotableSinglePhaseEnlist may not actually promote // the transaction when it is already delegated (this is // the way they resolve the race condition when two // threads attempt to delegate the same Lightweight // Transaction) In that case, we can safely ignore // our delegated transaction, and proceed to enlist // in the promoted one. if (tx.EnlistPromotableSinglePhase(delegatedTransaction)) { hasDelegatedTransaction = true; this.DelegatedTransaction = delegatedTransaction; if (Bid.AdvancedOn) { long transactionId = SqlInternalTransaction.NullTransactionId; int transactionObjectID = 0; if (null != CurrentTransaction) { transactionId = CurrentTransaction.TransactionId; transactionObjectID = CurrentTransaction.ObjectID; } Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, delegated to transaction %d# with transactionId=0x%I64x\n", base.ObjectID, transactionObjectID, transactionId); } } } catch (SqlException e) { // we do not want to eat the error if it is a fatal one if (e.Class >= TdsEnums.FATAL_ERROR_CLASS) { throw; } // if the parser is null or its state is not openloggedin, the connection is no longer good. SqlInternalConnectionTds tdsConnection = this as SqlInternalConnectionTds; if (tdsConnection != null) { TdsParser parser = tdsConnection.Parser; if (parser == null || parser.State != TdsParserState.OpenLoggedIn) { throw; } } ADP.TraceExceptionWithoutRethrow(e); // In this case, SqlDelegatedTransaction.Initialize // failed and we don't necessarily want to reject // things -- there may have been a legitimate reason // for the failure. } } if (!hasDelegatedTransaction) { if (Bid.AdvancedOn) { Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, delegation not possible, enlisting.\n", base.ObjectID); } byte[] cookie = null; if (null == _whereAbouts) { byte[] dtcAddress = GetDTCAddress(); if (null == dtcAddress) { throw SQL.CannotGetDTCAddress(); } _whereAbouts = dtcAddress; } cookie = GetTransactionCookie(tx, _whereAbouts); // send cookie to server to finish enlistment PropagateTransactionCookie(cookie); _isEnlistedInTransaction = true; if (Bid.AdvancedOn) { long transactionId = SqlInternalTransaction.NullTransactionId; int transactionObjectID = 0; if (null != CurrentTransaction) { transactionId = CurrentTransaction.TransactionId; transactionObjectID = CurrentTransaction.ObjectID; } Bid.Trace("<sc.SqlInternalConnection.EnlistNonNull|ADV> %d#, enlisted with transaction %d# with transactionId=0x%I64x\n", base.ObjectID, transactionObjectID, transactionId); } } EnlistedTransaction = tx; // Tell the base class about our enlistment // If we're on a Yukon or newer server, and we we delegate the // transaction successfully, we will have done a begin transaction, // which produces a transaction id that we should execute all requests // on. The TdsParser or SmiEventSink will store this information as // the current transaction. // // Likewise, propagating a transaction to a Yukon or newer server will // produce a transaction id that The TdsParser or SmiEventSink will // store as the current transaction. // // In either case, when we're working with a Yukon or newer server // we better have a current transaction by now. Debug.Assert(!IsYukonOrNewer || null != CurrentTransaction, "delegated/enlisted transaction with null current transaction?"); }
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 & Yukon, 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; } }
protected void Enlist(SysTx.Transaction tx) { // This method should not be called while the connection has a // reference to an active delegated transaction. // Manual enlistment via SqlConnection.EnlistTransaction // should catch this case and throw an exception. // // Automatic enlistment isn't possible because // Sys.Tx keeps the connection alive until the transaction is completed. Debug.Assert (!IsNonPoolableTransactionRoot, "cannot defect an active delegated transaction!"); // potential race condition, but it's an assert if (null == tx) { if (IsEnlistedInTransaction) { EnlistNull(); } else { // When IsEnlistedInTransaction is false, it means we are in one of two states: // 1. EnlistTransaction is null, so the connection is truly not enlisted in a transaction, or // 2. Connection is enlisted in a SqlDelegatedTransaction. // // For #2, we have to consider whether or not the delegated transaction is active. // If it is not active, we allow the enlistment in the NULL transaction. // // If it is active, technically this is an error. // However, no exception is thrown as this was the precedent (and this case is silently ignored, no error, but no enlistment either). // There are two mitigations for this: // 1. SqlConnection.EnlistTransaction checks that the enlisted transaction has completed before allowing a different enlistment. // 2. For debug builds, the assert at the beginning of this method checks for an enlistment in an active delegated transaction. SysTx.Transaction enlistedTransaction = EnlistedTransaction; if (enlistedTransaction != null && enlistedTransaction.TransactionInformation.Status != SysTx.TransactionStatus.Active) { EnlistNull(); } } } // Only enlist if it's different... else if (!tx.Equals(EnlistedTransaction)) { // WebData 20000024 - Must use Equals, not != EnlistNonNull(tx); } }
override protected void CleanupTransactionOnCompletion(SysTx.Transaction transaction) { // Note: unlocked, potentially multi-threaded code, so pull delegate to local to // ensure it doesn't change between test and call. SqlDelegatedTransaction delegatedTransaction = DelegatedTransaction; if (null != delegatedTransaction) { delegatedTransaction.TransactionEnded(transaction); } }
internal void EnlistTransactionInternal(SysTx.Transaction transaction) { OleDbConnection.VerifyExecutePermission(); SysTx.IDtcTransaction oleTxTransaction = ADP.GetOletxTransaction(transaction); IntPtr hscp; Bid.ScopeEnter(out hscp, "<oledb.ITransactionJoin.JoinTransaction|API|OLEDB> %d#\n", ObjectID); try { using(ITransactionJoinWrapper transactionJoin = ITransactionJoin()) { if (null == transactionJoin.Value) { throw ODB.TransactionsNotSupported(Provider, (Exception)null); } transactionJoin.Value.JoinTransaction(oleTxTransaction, (int) IsolationLevel.Unspecified, 0, IntPtr.Zero); _unEnlistDuringDeactivate = (null != transaction); } } finally { Bid.ScopeLeave(ref hscp); } EnlistedTransaction = transaction; }
// Detach transaction from connection. internal void DetachTransaction(SysTx.Transaction transaction, bool isExplicitlyReleasing) { Bid.Trace("<prov.DbConnectionInternal.DetachTransaction|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", ObjectID, _pooledCount); // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should // be the exception, not the rule. lock (this) { // Detach if detach-on-end behavior, or if outer connection was closed DbConnection owner = (DbConnection)Owner; if (isExplicitlyReleasing || UnbindOnTransactionCompletion || null == owner) { SysTx.Transaction currentEnlistedTransaction = _enlistedTransaction; if (currentEnlistedTransaction != null && transaction.Equals(currentEnlistedTransaction)) { EnlistedTransaction = null; if (IsTxRootWaitingForTxEnd) { DelegatedTransactionEnded(); } } } } }
internal void Open_EnlistTransaction(SysTx.Transaction transaction) { OdbcConnection.VerifyExecutePermission(); if ((null != this.weakTransaction) && this.weakTransaction.IsAlive) { throw ADP.LocalTransactionPresent(); } SysTx.IDtcTransaction oleTxTransaction = ADP.GetOletxTransaction(transaction); OdbcConnectionHandle connectionHandle = ConnectionHandle; ODBC32.RetCode retcode; if (null == oleTxTransaction) { retcode = connectionHandle.SetConnectionAttribute2(ODBC32.SQL_ATTR.SQL_COPT_SS_ENLIST_IN_DTC, (IntPtr) ODBC32.SQL_DTC_DONE, ODBC32.SQL_IS_PTR); } else { retcode = connectionHandle.SetConnectionAttribute4(ODBC32.SQL_ATTR.SQL_COPT_SS_ENLIST_IN_DTC, oleTxTransaction, ODBC32.SQL_IS_PTR); } if (retcode != ODBC32.RetCode.SUCCESS) { HandleError(connectionHandle, retcode); } // Tell the base class about our enlistment ((OdbcConnectionOpen)InnerConnection).EnlistedTransaction = transaction; }
// Handle transaction detach, pool cleanup and other post-transaction cleanup tasks associated with internal void CleanupConnectionOnTransactionCompletion(SysTx.Transaction transaction) { DetachTransaction(transaction, false); DbConnectionPool pool = Pool; if (null != pool) { pool.TransactionEnded(transaction, this); } }
override protected void Activate(SysTx.Transaction transaction) { throw ADP.ClosedConnectionError(); }
private void TransactionOutcomeEnlist(SysTx.Transaction transaction) { transaction.TransactionCompleted += new SysTx.TransactionCompletedEventHandler(TransactionCompletedEvent); }
override public void EnlistTransaction(SysTx.Transaction transaction) { throw ADP.ClosedConnectionError(); }
override protected void Activate(SysTx.Transaction transaction) { OdbcConnection.ExecutePermission.Demand(); }
abstract protected void Activate(SysTx.Transaction transaction);
//////////////////////////////////////////////////////////////////////////////////////// // POOLING METHODS //////////////////////////////////////////////////////////////////////////////////////// override protected void Activate(SysTx.Transaction transaction) { FailoverPermissionDemand(); // Demand for unspecified failover pooled connections // When we're required to automatically enlist in transactions and // there is one we enlist in it. On the other hand, if there isn't a // transaction and we are currently enlisted in one, then we // unenlist from it. // // Regardless of whether we're required to automatically enlist, // when there is not a current transaction, we cannot leave the // connection enlisted in a transaction. if (null != transaction){ if (ConnectionOptions.Enlist) { Enlist(transaction); } } else { Enlist(null); } }
override protected void Activate(SysTx.Transaction transaction) { throw ADP.NotSupported(); }