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();
 }