File: SqlInternalConnection.cs

package info (click to toggle)
mono 6.12.0.199%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,296,836 kB
  • sloc: cs: 11,181,803; xml: 2,850,076; ansic: 699,709; cpp: 123,344; perl: 59,361; javascript: 30,841; asm: 21,853; makefile: 20,405; sh: 15,009; python: 4,839; pascal: 925; sql: 859; sed: 16; php: 1
file content (717 lines) | stat: -rw-r--r-- 31,252 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
//------------------------------------------------------------------------------
// <copyright file="SqlInternalConnection.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------

namespace System.Data.SqlClient
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Common;
    using System.Data.ProviderBase;
    using System.Diagnostics;
    using System.Globalization;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Permissions;
    using System.Text;
    using System.Threading;
    using SysTx = System.Transactions;

    abstract internal class SqlInternalConnection : DbConnectionInternal {
        private readonly SqlConnectionString _connectionOptions;
        private bool                         _isEnlistedInTransaction; // is the server-side connection enlisted? true while we're enlisted, reset only after we send a null...
        private byte[]                       _promotedDTCToken;        // token returned by the server when we promote transaction
        private byte[]                       _whereAbouts;             // cache the whereabouts (DTC Address) for exporting
        
        private bool                         _isGlobalTransaction = false; // Whether this is a Global Transaction (Non-MSDTC, Azure SQL DB Transaction)
        private bool                         _isGlobalTransactionEnabledForServer = false; // Whether Global Transactions are enabled for this Azure SQL DB Server
        private static readonly Guid         _globalTransactionTMID = new Guid("1c742caf-6680-40ea-9c26-6b6846079764"); // ID of the Non-MSDTC, Azure SQL DB Transaction Manager
        
        // if connection is not open: null
        // if connection is open: currently active database
        internal string CurrentDatabase { get; set; }

        // if connection is not open yet, CurrentDataSource is null
        // if connection is open:
        // * for regular connections, it is set to Data Source value from connection string
        // * for connections with FailoverPartner, it is set to the FailoverPartner value from connection string if the connection was opened to it.
        internal string CurrentDataSource { get; set; }

        // the delegated (or promoted) transaction we're responsible for.
        internal SqlDelegatedTransaction DelegatedTransaction { get; set; }

        internal enum TransactionRequest {
            Begin,
            Promote,
            Commit,
            Rollback,
            IfRollback,
            Save
        };

        internal SqlInternalConnection(SqlConnectionString connectionOptions) : base() {
            Debug.Assert(null != connectionOptions, "null connectionOptions?");
            _connectionOptions = connectionOptions;
        }

        internal SqlConnection Connection {
            get {
                return (SqlConnection)Owner;
            }
        }

        internal SqlConnectionString ConnectionOptions {
            get {
                return _connectionOptions;
            }
        }

        abstract internal SqlInternalTransaction CurrentTransaction {
            get;
        }

        // SQLBU 415870
        //  Get the internal transaction that should be hooked to a new outer transaction
        //  during a BeginTransaction API call.  In some cases (i.e. connection is going to 
        //  be reset), CurrentTransaction should not be hooked up this way.
        virtual internal SqlInternalTransaction AvailableInternalTransaction {
            get {
                return CurrentTransaction;
            }
        }

        abstract internal SqlInternalTransaction PendingTransaction {
            get;
        }

        override protected internal bool IsNonPoolableTransactionRoot {
            get {
                return IsTransactionRoot;  // default behavior is that root transactions are NOT poolable.  Subclasses may override.
            }
        }

        override internal bool IsTransactionRoot {
            get {
                var delegatedTransaction = DelegatedTransaction;
                return ((null != delegatedTransaction) && (delegatedTransaction.IsActive));
            }
        }

        internal bool HasLocalTransaction {
            get {
                SqlInternalTransaction currentTransaction = CurrentTransaction;
                bool result = (null != currentTransaction && currentTransaction.IsLocal);
                return result;
            }
        }

        internal bool HasLocalTransactionFromAPI {
            get {
                SqlInternalTransaction currentTransaction = CurrentTransaction;
                bool result = (null != currentTransaction && currentTransaction.HasParentTransaction);
                return result;
            }
        }

        internal bool IsEnlistedInTransaction {
            get {
                return _isEnlistedInTransaction;
            }
        }

        abstract internal bool IsLockedForBulkCopy {
            get;            
        }

        abstract internal bool IsShiloh {
            get;
        }

        abstract internal bool IsYukonOrNewer {
            get;
        }

        abstract internal bool IsKatmaiOrNewer {
            get;
        }

        internal byte[] PromotedDTCToken {
            get {
                return _promotedDTCToken;
            }
            set {
                _promotedDTCToken = value;
            }
        }

        internal bool IsGlobalTransaction {
            get {
                return _isGlobalTransaction;
            }
            set {
                _isGlobalTransaction = value;
            }
        }

        internal bool IsGlobalTransactionsEnabledForServer {
            get {
                return _isGlobalTransactionEnabledForServer;
            }
            set {
                _isGlobalTransactionEnabledForServer = value;
            }
        }

        override public DbTransaction BeginTransaction(IsolationLevel iso) {
            return BeginSqlTransaction(iso, null, false);
        }

        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 ChangeDatabase(string database) {
            SqlConnection.ExecutePermission.Demand(); // MDAC 80961

            if (ADP.IsEmpty(database)) {
                throw ADP.EmptyDatabaseName();
            }

            ValidateConnectionForExecute(null); // 

            ChangeDatabaseInternal(database);  // do the real work...
        }

        abstract protected void ChangeDatabaseInternal(string database);
        
        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);
            }
        }

        override protected DbReferenceCollection CreateReferenceCollection() {
            return new SqlReferenceCollection();
        }

        override protected void Deactivate() {
            if (Bid.AdvancedOn) {
                Bid.Trace("<sc.SqlInternalConnection.Deactivate|ADV> %d# deactivating\n", base.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) {
                // 
                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);
            }
        }
       
        abstract internal void DisconnectTransaction(SqlInternalTransaction internalTransaction);

        override public void Dispose() {
            _whereAbouts = null;
            base.Dispose();
        }

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

        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.

                    // NOTE: Global Transactions is an Azure SQL DB only 
                    // feature where the Transaction Manager (TM) is not
                    // MS-DTC. Sys.Tx added APIs to support Non MS-DTC
                    // promoter types/TM in .NET 4.6.1. Following directions
                    // from .NETFX shiproom, to avoid a "hard-dependency"
                    // (compile time) on Sys.Tx, we use reflection to invoke
                    // the new APIs. Further, the _isGlobalTransaction flag
                    // indicates that this is an Azure SQL DB Transaction
                    // that could be promoted to a Global Transaction (it's
                    // always false for on-prem Sql Server). The Promote()
                    // call in SqlDelegatedTransaction makes sure that the
                    // right Sys.Tx.dll is loaded and that Global Transactions
                    // are actually allowed for this Azure SQL DB.

                    if (_isGlobalTransaction) {
                        if (SysTxForGlobalTransactions.EnlistPromotableSinglePhase == null) {
                            // This could be a local Azure SQL DB transaction. 
                            hasDelegatedTransaction = tx.EnlistPromotableSinglePhase(delegatedTransaction);
                        }
                        else {
                            hasDelegatedTransaction = (bool)SysTxForGlobalTransactions.EnlistPromotableSinglePhase.Invoke(tx, new object[] { delegatedTransaction, _globalTransactionTMID });
                        }
                    }
                    else {
                        // This is an MS-DTC distributed transaction
                        hasDelegatedTransaction = tx.EnlistPromotableSinglePhase(delegatedTransaction);
                    }

                    if (hasDelegatedTransaction) {

                        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 (_isGlobalTransaction) {
                    if (SysTxForGlobalTransactions.GetPromotedToken == null) {
                        throw SQL.UnsupportedSysTxForGlobalTransactions();
                    }

                    cookie = (byte[])SysTxForGlobalTransactions.GetPromotedToken.Invoke(tx, null);
                }
                else {
                    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?");
        }

        internal void EnlistNull() {
            if (Bid.AdvancedOn) {
                Bid.Trace("<sc.SqlInternalConnection.EnlistNull|ADV> %d#, unenlisting.\n", base.ObjectID);
            }

            // We were in a transaction, but now we are not - so send
            // message to server with empty transaction - confirmed proper
            // behavior from Sameet Agarwal
            //
            // The connection pooler maintains separate pools for enlisted
            // transactions, and only when that transaction is committed or
            // rolled back will those connections be taken from that
            // separate pool and returned to the general pool of connections
            // that are not affiliated with any transactions.  When this
            // occurs, we will have a new transaction of null and we are
            // required to send an empty transaction payload to the server.

            PropagateTransactionCookie(null);

            _isEnlistedInTransaction = false;
            EnlistedTransaction = null; // Tell the base class about our enlistment

            if (Bid.AdvancedOn) {
                Bid.Trace("<sc.SqlInternalConnection.EnlistNull|ADV> %d#, unenlisted.\n", base.ObjectID);
            }

            // The EnlistTransaction above will return an TransactionEnded event, 
            // which causes the TdsParser or SmiEventSink should to clear the
            // current transaction.
            //
            // In either case, when we're working with a Yukon or newer server 
            // we better not have a current transaction at this point.

            Debug.Assert(!IsYukonOrNewer || null == CurrentTransaction, "unenlisted transaction with non-null current transaction?");   // verify it!
        }
        
        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;
            }
        }

        abstract internal void ExecuteTransaction(TransactionRequest transactionRequest, string name, IsolationLevel iso, SqlInternalTransaction internalTransaction, bool isDelegateControlRequest);

        internal SqlDataReader FindLiveReader(SqlCommand command) {
            SqlDataReader reader = null;
            SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection;
            if (null != referenceCollection) {
                reader =  referenceCollection.FindLiveReader(command);
            }
            return reader;
        }

        internal SqlCommand FindLiveCommand(TdsParserStateObject stateObj) {
            SqlCommand command = null;
            SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection;
            if (null != referenceCollection) {
                command =  referenceCollection.FindLiveCommand(stateObj);
            }
            return command;
        }

        static internal TdsParser GetBestEffortCleanupTarget(SqlConnection connection) {
            if (null != connection) {
                SqlInternalConnectionTds innerConnection = (connection.InnerConnection as SqlInternalConnectionTds);
                if (null != innerConnection) {
                    return innerConnection.Parser;
                }
            }

            return null;
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        static internal void BestEffortCleanup(TdsParser target) {
            if (null != target) {
                target.BestEffortCleanup();
            }
        }

        abstract protected byte[] GetDTCAddress();

        static private byte[] GetTransactionCookie(SysTx.Transaction transaction, byte[] whereAbouts) {
            byte[] transactionCookie = null;
            if (null != transaction) {
                transactionCookie = SysTx.TransactionInterop.GetExportCookie(transaction, whereAbouts);
            }
            return transactionCookie;
        }

        virtual protected void InternalDeactivate() {
        }

        // If wrapCloseInAction is defined, then the action it defines will be run with the connection close action passed in as a parameter
        // The close action also supports being run asynchronously
        internal void OnError(SqlException exception, bool breakConnection, Action<Action> wrapCloseInAction = null) {
            if (breakConnection) {
                DoomThisConnection();
            }

            var connection = Connection;
            if (null != connection) {
                connection.OnError(exception, breakConnection, wrapCloseInAction);
            }
            else if (exception.Class >= TdsEnums.MIN_ERROR_CLASS) {
                // It is an error, and should be thrown.  Class of TdsEnums.MIN_ERROR_CLASS
                // or above is an error, below TdsEnums.MIN_ERROR_CLASS denotes an info message.
                throw exception;
            }
        }

        abstract protected void PropagateTransactionCookie(byte[] transactionCookie);
        
        abstract internal void ValidateConnectionForExecute(SqlCommand command);
    }
}