System.Data.SQLite
Check-in [9e2d3ca71a]
Not logged in

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

Overview
Comment:First shot at nested transactions and transaction enlistment
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sourceforge
Files: files | file ages | folders
SHA1: 9e2d3ca71a92ce163b7121321a294ceecd51c15f
User & Date: rmsimpson 2006-01-27 05:55:12
Context
2006-01-27
05:55
Transaction enlistment and nested transaction test added check-in: a15dd26217 user: rmsimpson tags: sourceforge
05:55
First shot at nested transactions and transaction enlistment check-in: 9e2d3ca71a user: rmsimpson tags: sourceforge
2006-01-24
01:59
no message check-in: 4651c69837 user: rmsimpson tags: sourceforge
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

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

    49     49       /// The SQL command text, broken into individual SQL statements as they are executed
    50     50       /// </summary>
    51     51       internal List<SQLiteStatement> _statementList;
    52     52       /// <summary>
    53     53       /// Unprocessed SQL text that has not been executed
    54     54       /// </summary>
    55     55       internal string _remainingText;
           56  +    /// <summary>
           57  +    /// Transaction associated with this command
           58  +    /// </summary>
           59  +    private SQLiteTransaction _transaction;
    56     60   
    57     61       ///<overloads>
    58     62       /// Constructs a new SQLiteCommand
    59     63       /// </overloads>
    60     64       /// <summary>
    61     65       /// Default constructor
    62     66       /// </summary>
................................................................................
   115    119       {
   116    120         _statementList = null;
   117    121         _isReaderOpen = false;
   118    122         _commandTimeout = 30;
   119    123         _parameterCollection = new SQLiteParameterCollection(this);
   120    124         _designTimeVisible = true;
   121    125         _updateRowSource = UpdateRowSource.FirstReturnedRecord;
          126  +      _transaction = null;
   122    127   
   123    128         if (strSql != null)
   124    129           CommandText = strSql;
   125    130   
   126    131         if (cnn != null)
   127    132           DbConnection = cnn;
   128    133       }
................................................................................
   348    353   
   349    354       /// <summary>
   350    355       /// The transaction associated with this command.  SQLite only supports one transaction per connection, so this property forwards to the
   351    356       /// command's underlying connection.
   352    357       /// </summary>
   353    358       public new SQLiteTransaction Transaction
   354    359       {
   355         -      get { return _cnn._activeTransaction; }
          360  +      get { return _transaction; }
   356    361         set
   357    362         {
   358    363           if (_cnn != null)
   359    364           {
   360         -          if (value != _cnn._activeTransaction && value != null)
   361         -            throw new ArgumentOutOfRangeException("SQLiteTransaction", "Transaction is for a different connection than the one associated with this Command");
          365  +          if (_isReaderOpen)
          366  +            throw new InvalidOperationException("Cannot set Transaction while a DataReader is active");
          367  +
          368  +          if (value != null)
          369  +          {
          370  +            if (value._cnn != _cnn)
          371  +              throw new ArgumentException("Transaction is not associated with the command's connection");
          372  +          }
          373  +          _transaction = value;
   362    374           }
   363    375           else if (value != null)
   364    376             throw new ArgumentOutOfRangeException("SQLiteTransaction", "Not associated with a connection");
   365    377         }
   366    378       }
   367    379   
   368    380       /// <summary>

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

   101    101       /// </summary>
   102    102       private ConnectionState     _connectionState;
   103    103       /// <summary>
   104    104       /// The connection string
   105    105       /// </summary>
   106    106       private string              _connectionString;
   107    107       /// <summary>
   108         -    /// One transaction allowed per connection please!
          108  +    /// Nesting level of the transactions open on the connection
   109    109       /// </summary>
   110         -    internal SQLiteTransaction  _activeTransaction;
          110  +    internal int                 _transactionLevel;
          111  +    /// <summary>
          112  +    /// Whether or not the connection is enlisted in a distrubuted transaction
          113  +    /// </summary>
          114  +    internal bool                _enlisted;
   111    115       /// <summary>
   112    116       /// The base SQLite object to interop with
   113    117       /// </summary>
   114    118       internal SQLiteBase          _sql;
   115    119       /// <summary>
   116    120       /// Commands associated with this connection
   117    121       /// </summary>
................................................................................
   203    207       }
   204    208   
   205    209       private void Initialize(string connectionString)
   206    210       {
   207    211         _sql = null;
   208    212         _connectionState = ConnectionState.Closed;
   209    213         _connectionString = "";
   210         -      _activeTransaction = null;
          214  +      _transactionLevel = 0;
          215  +      _enlisted = false;
   211    216         _commandList = new List<SQLiteCommand>();
   212    217   
   213    218         if (connectionString != null)
   214    219           ConnectionString = connectionString;
   215    220       }
   216    221   
   217    222       /// <summary>
................................................................................
   345    350       /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.</param>
   346    351       /// <returns>Returns a SQLiteTransaction object.</returns>
   347    352       public SQLiteTransaction BeginTransaction(bool deferredLock)
   348    353       {
   349    354         if (_connectionState != ConnectionState.Open)
   350    355           throw new InvalidOperationException();
   351    356   
   352         -      if (_activeTransaction != null)
   353         -        throw new ArgumentException("Transaction already pending");
   354         -
   355         -      _activeTransaction = new SQLiteTransaction(this, deferredLock);
   356         -      return _activeTransaction;
          357  +      return new SQLiteTransaction(this, deferredLock);
   357    358       }
   358    359   
   359    360       /// <summary>
   360    361       /// Creates a new SQLiteTransaction if one isn't already active on the connection.
   361    362       /// </summary>
   362    363       /// <param name="isolationLevel">SQLite doesn't support varying isolation levels, so this parameter is ignored.</param>
   363    364       /// <returns>Returns a SQLiteTransaction object.</returns>
................................................................................
   561    562         }
   562    563         KeyValuePair<string, string>[] ar = new KeyValuePair<string, string>[ls.Count];
   563    564         ls.CopyTo(ar, 0);
   564    565   
   565    566         // Return the array of key-value pairs
   566    567         return ar;
   567    568       }
          569  +
          570  +#if !PLATFORM_COMPACTFRAMEWORK
          571  +    public override void EnlistTransaction(System.Transactions.Transaction transaction)
          572  +    {
          573  +      if (_transactionLevel > 0 && transaction != null)
          574  +        throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");
          575  +
          576  +      if (_enlisted == true)
          577  +        throw new ArgumentException("Already enlisted in a transaction");
          578  +
          579  +      transaction.EnlistVolatile(new SQLiteEnlistment(this), System.Transactions.EnlistmentOptions.None);
          580  +    }
          581  +#endif
   568    582   
   569    583       /// <summary>
   570    584       /// Looks for a key in the array of key/values of the parameter string.  If not found, return the specified default value
   571    585       /// </summary>
   572    586       /// <param name="opts">The Key/Value pair array to look in</param>
   573    587       /// <param name="key">The key to find</param>
   574    588       /// <param name="defValue">The default value to return if the key is not found</param>
................................................................................
   625    639   
   626    640           _dataSource = System.IO.Path.GetFileNameWithoutExtension(strFile);
   627    641   
   628    642           _sql.Execute(String.Format(CultureInfo.InvariantCulture, "PRAGMA Synchronous={0}", FindKey(opts, "Synchronous", "Normal")));
   629    643           _sql.Execute(String.Format(CultureInfo.InvariantCulture, "PRAGMA Cache_Size={0}", FindKey(opts, "Cache Size", "2000")));
   630    644           if (String.Compare(":MEMORY:", strFile, true, CultureInfo.InvariantCulture) == 0)
   631    645             _sql.Execute(String.Format(CultureInfo.InvariantCulture, "PRAGMA Page_Size={0}", FindKey(opts, "Page Size", "1024")));
          646  +
          647  +#if !PLATFORM_COMPACTFRAMEWORK
          648  +        if (FindKey(opts, "Enlist", "Y").ToUpper()[0] == 'Y' && Transactions.Transaction.Current != null)
          649  +          EnlistTransaction(Transactions.Transaction.Current);
          650  +#endif
   632    651         }
   633    652         catch (SQLiteException)
   634    653         {
   635    654           OnStateChange(ConnectionState.Broken);
   636    655           throw;
   637    656         }
   638    657         OnStateChange(ConnectionState.Open);

Added System.Data.SQLite/SQLiteEnlistment.cs.

            1  +/********************************************************
            2  + * ADO.NET 2.0 Data Provider for SQLite Version 3.X
            3  + * Written by Robert Simpson (robert@blackcastlesoft.com)
            4  + * 
            5  + * Released to the public domain, use at your own risk!
            6  + ********************************************************/
            7  +
            8  +#if !PLATFORM_COMPACTFRAMEWORK
            9  +namespace System.Data.SQLite
           10  +{
           11  +  using System;
           12  +  using System.Data;
           13  +  using System.Data.Common;
           14  +  using System.Transactions;
           15  +
           16  +  internal class SQLiteEnlistment : IEnlistmentNotification
           17  +  {
           18  +    private SQLiteTransaction _transaction;
           19  +
           20  +    internal SQLiteEnlistment(SQLiteConnection cnn)
           21  +    {
           22  +      _transaction = cnn.BeginTransaction();
           23  +      _transaction.Connection._enlisted = true;
           24  +    }
           25  +
           26  +    #region IEnlistmentNotification Members
           27  +
           28  +    public void Commit(Enlistment enlistment)
           29  +    {
           30  +      try
           31  +      {
           32  +        _transaction.IsValid();
           33  +        _transaction.Connection._transactionLevel = 1;
           34  +        _transaction.Commit();
           35  +
           36  +        enlistment.Done();
           37  +      }
           38  +      finally
           39  +      {
           40  +        _transaction.Connection._enlisted = false;
           41  +        _transaction = null;
           42  +      }
           43  +    }
           44  +
           45  +    public void InDoubt(Enlistment enlistment)
           46  +    {
           47  +      enlistment.Done();
           48  +    }
           49  +
           50  +    public void Prepare(PreparingEnlistment preparingEnlistment)
           51  +    {
           52  +      try
           53  +      {
           54  +        _transaction.IsValid();
           55  +      }
           56  +      catch(Exception e)
           57  +      {
           58  +        preparingEnlistment.ForceRollback(e);
           59  +        return;
           60  +      }
           61  +      preparingEnlistment.Done();
           62  +    }
           63  +
           64  +    public void Rollback(Enlistment enlistment)
           65  +    {
           66  +      _transaction.Connection._enlisted = false;
           67  +      try
           68  +      {
           69  +        _transaction.Rollback();
           70  +        enlistment.Done();
           71  +      }
           72  +      finally
           73  +      {
           74  +        _transaction = null;
           75  +      }
           76  +    }
           77  +
           78  +    #endregion
           79  +  }
           80  +}
           81  +#endif // !PLATFORM_COMPACT_FRAMEWORK

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

    24     24       /// <summary>
    25     25       /// Constructs the transaction object, binding it to the supplied connection
    26     26       /// </summary>
    27     27       /// <param name="cnn">The connection to open a transaction on</param>
    28     28       /// <param name="deferredLock">TRUE to defer the writelock, or FALSE to lock immediately</param>
    29     29       internal SQLiteTransaction(SQLiteConnection cnn, bool deferredLock)
    30     30       {
    31         -      try
           31  +      _cnn = cnn;
           32  +      if (_cnn._transactionLevel++ == 0)
    32     33         {
    33         -        if (!deferredLock)
    34         -          cnn._sql.Execute("BEGIN IMMEDIATE");
    35         -        else
    36         -          cnn._sql.Execute("BEGIN");
    37         -
    38         -        _cnn = cnn;
    39         -      }
    40         -      catch (SQLiteException)
    41         -      {
    42         -        BaseDispose();
    43         -        throw;
           34  +        try
           35  +        {
           36  +          if (!deferredLock)
           37  +            cnn._sql.Execute("BEGIN IMMEDIATE");
           38  +          else
           39  +            cnn._sql.Execute("BEGIN");
           40  +        }
           41  +        catch (SQLiteException)
           42  +        {
           43  +          _cnn._transactionLevel--;
           44  +          _cnn = null;
           45  +          throw;
           46  +        }
    44     47         }
    45     48       }
    46     49   
    47     50       /// <summary>
    48     51       /// Commits the current transaction.
    49     52       /// </summary>
    50     53       public override void Commit()
    51     54       {
    52         -      if (_cnn == null)
    53         -        throw new ArgumentNullException();
           55  +      IsValid();
    54     56   
    55         -      try
           57  +      if (--_cnn._transactionLevel == 0)
    56     58         {
    57         -        _cnn._sql.Execute("COMMIT");
    58         -      }
    59         -      catch (SQLiteException)
    60         -      {
    61         -        BaseDispose();
    62         -        throw;
           59  +        try
           60  +        {
           61  +          _cnn._sql.Execute("COMMIT");
           62  +        }
           63  +        finally
           64  +        {
           65  +          _cnn = null;
           66  +        }
    63     67         }
    64         -      BaseDispose();
           68  +      else
           69  +      {
           70  +        _cnn = null;
           71  +      }
    65     72       }
    66     73   
    67     74       /// <summary>
    68     75       /// Returns the underlying connection to which this transaction applies.
    69     76       /// </summary>
    70     77       public new SQLiteConnection Connection
    71     78       {
................................................................................
    84     91       /// Disposes the transaction.  If it is currently active, any changes are rolled back.
    85     92       /// </summary>
    86     93       protected override void Dispose(bool disposing)
    87     94       {
    88     95         if (_cnn != null) 
    89     96           Rollback();
    90     97   
    91         -      _cnn = null;
    92         -
    93     98         base.Dispose(disposing);
    94     99       }
    95    100   
    96    101       /// <summary>
    97    102       /// Gets the isolation level of the transaction.  SQLite does not support isolation levels, so this always returns Unspecified.
    98    103       /// </summary>
    99    104       public override IsolationLevel IsolationLevel
................................................................................
   102    107       }
   103    108   
   104    109       /// <summary>
   105    110       /// Rolls back the active transaction.
   106    111       /// </summary>
   107    112       public override void Rollback()
   108    113       {
   109         -      if (_cnn == null)
   110         -        throw new ArgumentNullException();
          114  +      IsValid();
   111    115   
   112    116         try
   113    117         {
   114    118           _cnn._sql.Execute("ROLLBACK");
          119  +        _cnn._transactionLevel = 0;
   115    120         }
   116         -      catch (SQLiteException)
          121  +      finally
   117    122         {
   118         -        BaseDispose();
   119         -        throw;
          123  +        _cnn = null;
   120    124         }
   121         -      BaseDispose();
   122    125       }
   123    126   
   124         -    private void BaseDispose()
          127  +    internal void IsValid()
   125    128       {
   126         -      _cnn._activeTransaction = null;
   127         -      _cnn = null;
          129  +      if (_cnn == null)
          130  +        throw new ArgumentNullException("No connection associated with this transaction");
          131  +
          132  +      if (_cnn._transactionLevel == 0)
          133  +      {
          134  +        _cnn = null;
          135  +        throw new SQLiteException((int)SQLiteErrorCode.Misuse, "No transactions active on this connection");
          136  +      }
   128    137       }
   129    138     }
   130    139   }

Changes to System.Data.SQLite/System.Data.SQLite.

    40     40       <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
    41     41       <ErrorReport>prompt</ErrorReport>
    42     42       <FileAlignment>512</FileAlignment>
    43     43     </PropertyGroup>
    44     44     <ItemGroup>
    45     45       <Reference Include="System" />
    46     46       <Reference Include="System.Data" />
           47  +    <Reference Include="System.Transactions" />
    47     48       <Reference Include="System.Xml" />
    48     49     </ItemGroup>
    49     50     <ItemGroup>
    50     51       <Compile Include="AssemblyInfo.cs" />
    51     52       <Compile Include="SQLite3.cs" />
    52     53       <Compile Include="SQLite3_UTF16.cs" />
    53     54       <Compile Include="SQLiteBase.cs" />
................................................................................
    62     63       </Compile>
    63     64       <Compile Include="SQLiteConnectionStringBuilder.cs" />
    64     65       <Compile Include="SQLiteConvert.cs" />
    65     66       <Compile Include="SQLiteDataAdapter.cs">
    66     67         <SubType>Component</SubType>
    67     68       </Compile>
    68     69       <Compile Include="SQLiteDataReader.cs" />
           70  +    <Compile Include="SQLiteEnlistment.cs" />
    69     71       <Compile Include="SQLiteException.cs" />
    70     72       <Compile Include="SQLiteFactory.cs" />
    71     73       <Compile Include="SQLiteFunction.cs" />
    72     74       <Compile Include="SQLiteFunctionAttribute.cs" />
    73     75       <Compile Include="SQLiteParameter.cs" />
    74     76       <Compile Include="SQLiteParameterCollection.cs" />
    75     77       <Compile Include="SQLiteStatement.cs" />