System.Data.SQLite
Check-in [7d43f65e6a]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Transaction Scope fixes
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sourceforge
Files: files | file ages | folders
SHA1: 7d43f65e6aaaf9ca5f9ff2a14347cc69171c8cac
User & Date: rmsimpson 2006-10-05 22:35:22
Context
2006-10-12
21:34
3.3.8 Codemerge check-in: 79005e2d23 user: rmsimpson tags: sourceforge
2006-10-05
22:35
Transaction Scope fixes check-in: 7d43f65e6a user: rmsimpson tags: sourceforge
21:18
Work in progress check-in: 230b880a4c user: rmsimpson tags: sourceforge
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to System.Data.SQLite/SQLiteConnection.cs.

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
455
456
457
458
459
460
461
















462



463
464
465
466
467
468
469
...
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
    /// Nesting level of the transactions open on the connection
    /// </summary>
    internal int                 _transactionLevel;
#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// Whether or not the connection is enlisted in a distrubuted transaction
    /// </summary>
    internal System.Transactions.Transaction _enlistment;
#endif
    /// <summary>
    /// The base SQLite object to interop with
    /// </summary>
    internal SQLiteBase          _sql;
    /// <summary>
    /// Commands associated with this connection
................................................................................
      {
        int x = _commandList.Count;
        for (int n = 0; n < x; n++)
        {
          _commandList[n].ClearCommands();
        }

















        _sql.Close();



        _sql = null;
      }

      OnStateChange(ConnectionState.Closed);
    }

    /// <summary>
................................................................................
    /// </summary>
    /// <param name="transaction">The distributed transaction to enlist in</param>
    public override void EnlistTransaction(System.Transactions.Transaction transaction)
    {
      if (_transactionLevel > 0 && transaction != null)
        throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");

      if (_enlistment != null && transaction != _enlistment)
        throw new ArgumentException("Already enlisted in a transaction");

      transaction.EnlistVolatile(new SQLiteEnlistment(this), System.Transactions.EnlistmentOptions.None);

      _enlistment = transaction;
    }
#endif

    /// <summary>
    /// Looks for a key in the array of key/values of the parameter string.  If not found, return the specified default value
    /// </summary>
    /// <param name="opts">The Key/Value pair array to look in</param>







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>







 







|


|
<
<







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
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
...
665
666
667
668
669
670
671
672
673
674
675


676
677
678
679
680
681
682
    /// Nesting level of the transactions open on the connection
    /// </summary>
    internal int                 _transactionLevel;
#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// Whether or not the connection is enlisted in a distrubuted transaction
    /// </summary>
    internal SQLiteEnlistment _enlistment;
#endif
    /// <summary>
    /// The base SQLite object to interop with
    /// </summary>
    internal SQLiteBase          _sql;
    /// <summary>
    /// Commands associated with this connection
................................................................................
      {
        int x = _commandList.Count;
        for (int n = 0; n < x; n++)
        {
          _commandList[n].ClearCommands();
        }

        if (_enlistment != null)
        {
          // If the connection is enlisted in a transaction scope and the scope is still active,
          // we cannot truly shut down this connection until the scope has completed.  Therefore make a 
          // hidden connection temporarily to hold open the connection until the scope has completed.
          SQLiteConnection cnn = new SQLiteConnection();
          cnn._sql = _sql;
          cnn._transactionLevel = _transactionLevel;
          cnn._enlistment = _enlistment;
          cnn._connectionState = _connectionState;
          
          cnn._enlistment._transaction._cnn = cnn;
          cnn._enlistment._disposeConnection = true;
        }
        else
        {
          _sql.Close();
        }

        _enlistment = null;
        _sql = null;
      }

      OnStateChange(ConnectionState.Closed);
    }

    /// <summary>
................................................................................
    /// </summary>
    /// <param name="transaction">The distributed transaction to enlist in</param>
    public override void EnlistTransaction(System.Transactions.Transaction transaction)
    {
      if (_transactionLevel > 0 && transaction != null)
        throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");

      if (_enlistment != null && transaction != _enlistment._scope)
        throw new ArgumentException("Already enlisted in a transaction");

      _enlistment = new SQLiteEnlistment(this, transaction);


    }
#endif

    /// <summary>
    /// Looks for a key in the array of key/values of the parameter string.  If not found, return the specified default value
    /// </summary>
    /// <param name="opts">The Key/Value pair array to look in</param>

Changes to System.Data.SQLite/SQLiteEnlistment.cs.

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
..
58
59
60
61
62
63
64

65

66
67
68
69
70
71
72



73

74
75
76
77
78
79
80
  using System;
  using System.Data;
  using System.Data.Common;
  using System.Transactions;

  internal class SQLiteEnlistment : IEnlistmentNotification
  {
    private SQLiteTransaction _transaction;



    internal SQLiteEnlistment(SQLiteConnection cnn)
    {
      _transaction = cnn.BeginTransaction();




    }

    #region IEnlistmentNotification Members

    public void Commit(Enlistment enlistment)
    {

      _transaction.Connection._enlistment = null;

      try
      {
        _transaction.IsValid();
        _transaction.Connection._transactionLevel = 1;
        _transaction.Commit();

        enlistment.Done();
      }
      finally
      {



        _transaction = null;

      }
    }

    public void InDoubt(Enlistment enlistment)
    {
      enlistment.Done();
    }
................................................................................
        return;
      }
      preparingEnlistment.Prepared();
    }

    public void Rollback(Enlistment enlistment)
    {

      _transaction.Connection._enlistment = null;

      try
      {
        _transaction.Rollback();
        enlistment.Done();
      }
      finally
      {



        _transaction = null;

      }
    }

    #endregion
  }
}
#endif // !PLATFORM_COMPACT_FRAMEWORK







|
>
>

|


>
>
>
>






>
|
>










>
>
>

>







 







>
|
>







>
>
>

>







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
..
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
  using System;
  using System.Data;
  using System.Data.Common;
  using System.Transactions;

  internal class SQLiteEnlistment : IEnlistmentNotification
  {
    internal SQLiteTransaction _transaction;
    internal Transaction _scope;
    internal bool _disposeConnection;

    internal SQLiteEnlistment(SQLiteConnection cnn, Transaction scope)
    {
      _transaction = cnn.BeginTransaction();
      _scope = scope;
      _disposeConnection = false;

      _scope.EnlistVolatile(this, System.Transactions.EnlistmentOptions.None);
    }

    #region IEnlistmentNotification Members

    public void Commit(Enlistment enlistment)
    {
      SQLiteConnection cnn = _transaction.Connection;
      cnn._enlistment = null;

      try
      {
        _transaction.IsValid();
        _transaction.Connection._transactionLevel = 1;
        _transaction.Commit();

        enlistment.Done();
      }
      finally
      {
        if (_disposeConnection)
          cnn.Dispose();

        _transaction = null;
        _scope = null;
      }
    }

    public void InDoubt(Enlistment enlistment)
    {
      enlistment.Done();
    }
................................................................................
        return;
      }
      preparingEnlistment.Prepared();
    }

    public void Rollback(Enlistment enlistment)
    {
      SQLiteConnection cnn = _transaction.Connection;
      cnn._enlistment = null;

      try
      {
        _transaction.Rollback();
        enlistment.Done();
      }
      finally
      {
        if (_disposeConnection)
          cnn.Dispose();

        _transaction = null;
        _scope = null;
      }
    }

    #endregion
  }
}
#endif // !PLATFORM_COMPACT_FRAMEWORK

Changes to test/TestCases.cs.

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
      Console.WriteLine("\r\nTests Finished.");
    }

    internal static void TransactionTest(DbConnection cnn)
    {
      using (TransactionScope scope = new TransactionScope())
      {
        cnn.EnlistTransaction(Transaction.Current);

        using (DbCommand cmd = cnn.CreateCommand())
        {

          cmd.CommandText = "CREATE TABLE VolatileTable (ID INTEGER PRIMARY KEY, MyValue VARCHAR(50))";
          cmd.ExecuteNonQuery();

          using (DbCommand cmd2 = cnn.CreateCommand())
          {
            using (cmd2.Transaction = cnn.BeginTransaction())
            {

              cmd2.CommandText = "INSERT INTO VolatileTable (ID, MyValue) VALUES(1, 'Hello')";
              cmd2.ExecuteNonQuery();
              cmd2.Transaction.Commit();
            }
          }
        }

      }




      using (DbCommand cmd = cnn.CreateCommand())
      {
        cmd.CommandText = "SELECT COUNT(*) FROM VolatileTable";
        try
        {
          object o = cmd.ExecuteScalar();
          throw new InvalidOperationException("Transaction failed! The table exists!");







|
|
|
|
>
|
|
>
|
|
|
|
>
|
|
|
|
|
|
>
|
>
>
>
>







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
      Console.WriteLine("\r\nTests Finished.");
    }

    internal static void TransactionTest(DbConnection cnn)
    {
      using (TransactionScope scope = new TransactionScope())
      {
        using (DbConnection cnn2 = ((ICloneable)cnn).Clone() as DbConnection)
        {
          using (DbCommand cmd = cnn2.CreateCommand())
          {
            // Created a table inside the transaction scope
            cmd.CommandText = "CREATE TABLE VolatileTable (ID INTEGER PRIMARY KEY, MyValue VARCHAR(50))";
            cmd.ExecuteNonQuery();

            using (DbCommand cmd2 = cnn2.CreateCommand())
            {
              using (cmd2.Transaction = cnn2.BeginTransaction())
              {
                // Inserting a value inside the table, inside a transaction which is inside the transaction scope
                cmd2.CommandText = "INSERT INTO VolatileTable (ID, MyValue) VALUES(1, 'Hello')";
                cmd2.ExecuteNonQuery();
                cmd2.Transaction.Commit();
              }
            }
          }
          // Connection is disposed before the transactionscope leaves, thereby forcing the connection to stay open
        }
        // Exit the transactionscope without committing it, causing a rollback of both the create table and the insert
      }

      // Verify that the table does not exist
      using (DbCommand cmd = cnn.CreateCommand())
      {
        cmd.CommandText = "SELECT COUNT(*) FROM VolatileTable";
        try
        {
          object o = cmd.ExecuteScalar();
          throw new InvalidOperationException("Transaction failed! The table exists!");