System.Data.SQLite
Check-in [79365bcaed]
Not logged in

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

Overview
Comment:Add support for mapping transaction isolation levels to their legacy default values. Pursuant to [56b42d99c1].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | isolationLevels
Files: files | file ages | folders
SHA1: 79365bcaed517b41e5eccc8af3e1f0981a8afd5a
User & Date: mistachkin 2014-03-30 01:19:01
Original User & Date: denika 2014-03-30 01:19:01
Context
2014-03-30
01:42
Update version history docs. Closed-Leaf check-in: a9fb57516e user: mistachkin tags: isolationLevels
01:19
Add support for mapping transaction isolation levels to their legacy default values. Pursuant to [56b42d99c1]. check-in: 79365bcaed user: mistachkin tags: isolationLevels
2014-03-29
20:52
Make test 'data-1.37' more resilient to thread timing issues. check-in: a242748e03 user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

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

  1037   1037         /// <summary>
  1038   1038         /// When the <see cref="SQLiteDataReader.HasRows" /> property is used, it
  1039   1039         /// should return non-zero if there were ever any rows in the associated
  1040   1040         /// result sets.
  1041   1041         /// </summary>
  1042   1042         StickyHasRows = 0x400000,
  1043   1043   
         1044  +      /// <summary>
         1045  +      /// Enable "strict" transaction enlistment semantics.  Setting this flag
         1046  +      /// will cause an exception to be thrown if an attempt is made to enlist
         1047  +      /// in a transaction with an unavailable or unsupported isolation level.
         1048  +      /// In the future, more extensive checks may be enabled by this flag as
         1049  +      /// well.
         1050  +      /// </summary>
         1051  +      StrictEnlistment = 0x800000,
         1052  +
         1053  +      /// <summary>
         1054  +      /// Enable mapping of unsupported transaction isolation levels to the
         1055  +      /// closest supported transaction isolation level.
         1056  +      /// </summary>
         1057  +      MapIsolationLevels = 0x1000000,
         1058  +
  1044   1059         /// <summary>
  1045   1060         /// When binding parameter values or returning column values, always
  1046   1061         /// treat them as though they were plain text (i.e. no numeric,
  1047   1062         /// date/time, or other conversions should be attempted).
  1048   1063         /// </summary>
  1049   1064         BindAndGetAllAsText = BindAllAsText | GetAllAsText,
  1050   1065   

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

   334    334       /// because those names are reserved for use by SQLite (i.e. they cannot
   335    335       /// be confused with the names of user objects).
   336    336       /// </summary>
   337    337       internal const string DefaultBaseSchemaName = "sqlite_default_schema";
   338    338   
   339    339       private const string MemoryFileName = ":memory:";
   340    340   
          341  +    internal const IsolationLevel DeferredIsolationLevel = IsolationLevel.ReadCommitted;
          342  +    internal const IsolationLevel ImmediateIsolationLevel = IsolationLevel.Serializable;
          343  +
   341    344       private const SQLiteConnectionFlags DefaultFlags = SQLiteConnectionFlags.Default;
   342    345       private const SQLiteSynchronousEnum DefaultSynchronous = SQLiteSynchronousEnum.Default;
   343    346       private const SQLiteJournalModeEnum DefaultJournalMode = SQLiteJournalModeEnum.Default;
   344    347       private const IsolationLevel DefaultIsolationLevel = IsolationLevel.Serializable;
   345    348       private const SQLiteDateFormats DefaultDateTimeFormat = SQLiteDateFormats.ISO8601;
   346    349       private const DateTimeKind DefaultDateTimeKind = DateTimeKind.Unspecified;
   347    350       private const string DefaultDateTimeFormatString = null;
................................................................................
  1237   1240       /// Determines and returns the fallback default isolation level when one cannot be
  1238   1241       /// obtained from an existing connection instance.
  1239   1242       /// </summary>
  1240   1243       /// <returns>
  1241   1244       /// The fallback default isolation level for this connection instance -OR-
  1242   1245       /// <see cref="IsolationLevel.Unspecified" /> if it cannot be determined.
  1243   1246       /// </returns>
  1244         -    internal static IsolationLevel GetFallbackDefaultIsolationLevel()
         1247  +    private static IsolationLevel GetFallbackDefaultIsolationLevel()
  1245   1248       {
  1246   1249           return DefaultIsolationLevel;
  1247   1250       }
  1248   1251   
  1249   1252       /// <summary>
  1250   1253       /// Determines and returns the default isolation level for this connection instance.
  1251   1254       /// </summary>
................................................................................
  1267   1270       /// When FALSE, a writelock is obtained immediately.  The default is TRUE, but in a multi-threaded multi-writer
  1268   1271       /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.</param>
  1269   1272       /// <returns>Returns a SQLiteTransaction object.</returns>
  1270   1273       [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")]
  1271   1274       public SQLiteTransaction BeginTransaction(IsolationLevel isolationLevel, bool deferredLock)
  1272   1275       {
  1273   1276         CheckDisposed();
  1274         -      return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? IsolationLevel.Serializable : IsolationLevel.ReadCommitted);
         1277  +      return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? ImmediateIsolationLevel : DeferredIsolationLevel);
  1275   1278       }
  1276   1279   
  1277   1280       /// <summary>
  1278   1281       /// OBSOLETE.  Creates a new SQLiteTransaction if one isn't already active on the connection.
  1279   1282       /// </summary>
  1280   1283       /// <param name="deferredLock">When TRUE, SQLite defers obtaining a write lock until a write operation is requested.
  1281   1284       /// When FALSE, a writelock is obtained immediately.  The default is false, but in a multi-threaded multi-writer
  1282   1285       /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.</param>
  1283   1286       /// <returns>Returns a SQLiteTransaction object.</returns>
  1284   1287       [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")]
  1285   1288       public SQLiteTransaction BeginTransaction(bool deferredLock)
  1286   1289       {
  1287   1290         CheckDisposed();
  1288         -      return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? IsolationLevel.Serializable : IsolationLevel.ReadCommitted);
         1291  +      return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? ImmediateIsolationLevel : DeferredIsolationLevel);
  1289   1292       }
  1290   1293   
  1291   1294       /// <summary>
  1292   1295       /// Creates a new <see cref="SQLiteTransaction" /> if one isn't already active on the connection.
  1293   1296       /// </summary>
  1294   1297       /// <param name="isolationLevel">Supported isolation levels are Serializable, ReadCommitted and Unspecified.</param>
  1295   1298       /// <remarks>
................................................................................
  1326   1329       /// <returns></returns>
  1327   1330       protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
  1328   1331       {
  1329   1332         if (_connectionState != ConnectionState.Open)
  1330   1333           throw new InvalidOperationException();
  1331   1334   
  1332   1335         if (isolationLevel == IsolationLevel.Unspecified) isolationLevel = _defaultIsolation;
         1336  +      isolationLevel = GetEffectiveIsolationLevel(isolationLevel);
  1333   1337   
  1334         -      if (isolationLevel != IsolationLevel.Serializable && isolationLevel != IsolationLevel.ReadCommitted)
         1338  +      if (isolationLevel != ImmediateIsolationLevel && isolationLevel != DeferredIsolationLevel)
  1335   1339           throw new ArgumentException("isolationLevel");
  1336   1340   
  1337   1341         SQLiteTransaction transaction =
  1338         -          new SQLiteTransaction(this, isolationLevel != IsolationLevel.Serializable);
         1342  +          new SQLiteTransaction(this, isolationLevel != ImmediateIsolationLevel);
  1339   1343   
  1340   1344         OnChanged(this, new ConnectionEventArgs(
  1341   1345             SQLiteConnectionEventType.NewTransaction, null, transaction,
  1342   1346             null, null, null, null, null));
  1343   1347   
  1344   1348         return transaction;
  1345   1349       }
................................................................................
  1859   1863           throw new ArgumentException("Already enlisted in a transaction");
  1860   1864   
  1861   1865         if (_transactionLevel > 0 && transaction != null)
  1862   1866           throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");
  1863   1867         else if (transaction == null)
  1864   1868           throw new ArgumentNullException("Unable to enlist in transaction, it is null");
  1865   1869   
  1866         -      _enlistment = new SQLiteEnlistment(this, transaction);
         1870  +      bool strictEnlistment = ((_flags & SQLiteConnectionFlags.StrictEnlistment) ==
         1871  +          SQLiteConnectionFlags.StrictEnlistment);
         1872  +
         1873  +      _enlistment = new SQLiteEnlistment(this, transaction,
         1874  +          GetFallbackDefaultIsolationLevel(), strictEnlistment,
         1875  +          strictEnlistment);
  1867   1876   
  1868   1877         OnChanged(this, new ConnectionEventArgs(
  1869   1878             SQLiteConnectionEventType.EnlistTransaction, null, null, null, null,
  1870   1879             null, null, new object[] { _enlistment }));
  1871   1880       }
  1872   1881   #endif
  1873   1882   
................................................................................
  2178   2187   
  2179   2188               if ((_flags & SQLiteConnectionFlags.NoConnectionPool) == SQLiteConnectionFlags.NoConnectionPool)
  2180   2189                   result = false;
  2181   2190           }
  2182   2191   
  2183   2192           return result;
  2184   2193       }
         2194  +
         2195  +    /// <summary>
         2196  +    /// Determines the transaction isolation level that should be used by
         2197  +    /// the caller, primarily based upon the one specified by the caller.
         2198  +    /// If mapping of transaction isolation levels is enabled, the returned
         2199  +    /// transaction isolation level may be significantly different than the
         2200  +    /// originally specified one.
         2201  +    /// </summary>
         2202  +    /// <param name="isolationLevel">
         2203  +    /// The originally specified transaction isolation level.
         2204  +    /// </param>
         2205  +    /// <returns>
         2206  +    /// The transaction isolation level that should be used.
         2207  +    /// </returns>
         2208  +    private IsolationLevel GetEffectiveIsolationLevel(
         2209  +        IsolationLevel isolationLevel
         2210  +        )
         2211  +    {
         2212  +        if ((_flags & SQLiteConnectionFlags.MapIsolationLevels)
         2213  +                != SQLiteConnectionFlags.MapIsolationLevels)
         2214  +        {
         2215  +            return isolationLevel;
         2216  +        }
         2217  +
         2218  +        switch (isolationLevel)
         2219  +        {
         2220  +            case IsolationLevel.Unspecified:
         2221  +            case IsolationLevel.Chaos:
         2222  +            case IsolationLevel.ReadUncommitted:
         2223  +            case IsolationLevel.ReadCommitted:
         2224  +                return DeferredIsolationLevel;
         2225  +            case IsolationLevel.RepeatableRead:
         2226  +            case IsolationLevel.Serializable:
         2227  +            case IsolationLevel.Snapshot:
         2228  +                return ImmediateIsolationLevel;
         2229  +            default:
         2230  +                return GetFallbackDefaultIsolationLevel();
         2231  +        }
         2232  +    }
  2185   2233   
  2186   2234       /// <summary>
  2187   2235       /// Opens the connection using the parameters found in the <see cref="ConnectionString" />.
  2188   2236       /// </summary>
  2189   2237       public override void Open()
  2190   2238       {
  2191   2239         CheckDisposed();
................................................................................
  2280   2328           bool usePooling = SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", GetDefaultPooling().ToString()));
  2281   2329           int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", DefaultMaxPoolSize.ToString()), CultureInfo.InvariantCulture);
  2282   2330   
  2283   2331           _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", DefaultConnectionTimeout.ToString()), CultureInfo.InvariantCulture);
  2284   2332   
  2285   2333           enumValue = TryParseEnum(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", DefaultIsolationLevel.ToString()), true);
  2286   2334           _defaultIsolation = (enumValue is IsolationLevel) ? (IsolationLevel)enumValue : DefaultIsolationLevel;
         2335  +        _defaultIsolation = GetEffectiveIsolationLevel(_defaultIsolation);
  2287   2336   
  2288         -        if (_defaultIsolation != IsolationLevel.Serializable && _defaultIsolation != IsolationLevel.ReadCommitted)
         2337  +        if (_defaultIsolation != ImmediateIsolationLevel && _defaultIsolation != DeferredIsolationLevel)
  2289   2338             throw new NotSupportedException("Invalid Default IsolationLevel specified");
  2290   2339   
  2291   2340           _baseSchemaName = FindKey(opts, "BaseSchemaName", DefaultBaseSchemaName);
  2292   2341   
  2293   2342           if (_sql == null)
  2294   2343           {
  2295   2344               SetupSQLiteBase(opts);

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

     4      4    *
     5      5    * Released to the public domain, use at your own risk!
     6      6    ********************************************************/
     7      7   
     8      8   #if !PLATFORM_COMPACTFRAMEWORK
     9      9   namespace System.Data.SQLite
    10     10   {
    11         -  using System.Transactions;
           11  +    using System.Globalization;
           12  +    using System.Transactions;
    12     13   
    13     14     internal sealed class SQLiteEnlistment : IDisposable, IEnlistmentNotification
    14     15     {
    15     16       internal SQLiteTransaction _transaction;
    16     17       internal Transaction _scope;
    17     18       internal bool _disposeConnection;
    18     19   
    19         -    internal SQLiteEnlistment(SQLiteConnection cnn, Transaction scope)
           20  +    internal SQLiteEnlistment(
           21  +        SQLiteConnection cnn,
           22  +        Transaction scope,
           23  +        System.Data.IsolationLevel defaultIsolationLevel,
           24  +        bool throwOnUnavailable,
           25  +        bool throwOnUnsupported
           26  +        )
    20     27       {
    21     28         _transaction = cnn.BeginTransaction(GetSystemDataIsolationLevel(
    22         -          cnn, scope));
           29  +          cnn, scope, defaultIsolationLevel, throwOnUnavailable,
           30  +          throwOnUnsupported));
    23     31   
    24     32         _scope = scope;
    25     33   
    26     34         _scope.EnlistVolatile(this, System.Transactions.EnlistmentOptions.None);
    27     35       }
    28     36   
    29     37       ///////////////////////////////////////////////////////////////////////////
    30     38   
    31     39       #region Private Methods
    32     40       private System.Data.IsolationLevel GetSystemDataIsolationLevel(
    33     41           SQLiteConnection connection,
    34         -        Transaction transaction
           42  +        Transaction transaction,
           43  +        System.Data.IsolationLevel defaultIsolationLevel,
           44  +        bool throwOnUnavailable,
           45  +        bool throwOnUnsupported
    35     46           )
    36     47       {
    37     48           if (transaction == null)
    38     49           {
    39     50               //
    40         -            // TODO: Perhaps throw an exception here if the connection
    41         -            //       is null?
           51  +            // NOTE: If neither the transaction nor connection isolation
           52  +            //       level is available, throw an exception if instructed
           53  +            //       by the caller.
    42     54               //
    43         -            return (connection != null) ?
    44         -                connection.GetDefaultIsolationLevel() :
    45         -                SQLiteConnection.GetFallbackDefaultIsolationLevel();
           55  +            if (connection != null)
           56  +                return connection.GetDefaultIsolationLevel();
           57  +
           58  +            if (throwOnUnavailable)
           59  +            {
           60  +                throw new InvalidOperationException(
           61  +                    "isolation level is unavailable");
           62  +            }
           63  +
           64  +            return defaultIsolationLevel;
    46     65           }
    47     66   
    48     67           System.Transactions.IsolationLevel isolationLevel =
    49     68               transaction.IsolationLevel;
    50     69   
    51     70           //
    52     71           // TODO: Are these isolation level mappings actually correct?
    53     72           //
    54     73           switch (isolationLevel)
    55     74           {
           75  +            case IsolationLevel.Unspecified:
           76  +                return System.Data.IsolationLevel.Unspecified;
    56     77               case IsolationLevel.Chaos:
    57     78                   return System.Data.IsolationLevel.Chaos;
    58         -            case IsolationLevel.ReadCommitted:
    59         -                return System.Data.IsolationLevel.ReadCommitted;
    60     79               case IsolationLevel.ReadUncommitted:
    61     80                   return System.Data.IsolationLevel.ReadUncommitted;
           81  +            case IsolationLevel.ReadCommitted:
           82  +                return System.Data.IsolationLevel.ReadCommitted;
    62     83               case IsolationLevel.RepeatableRead:
    63     84                   return System.Data.IsolationLevel.RepeatableRead;
    64     85               case IsolationLevel.Serializable:
    65     86                   return System.Data.IsolationLevel.Serializable;
    66     87               case IsolationLevel.Snapshot:
    67     88                   return System.Data.IsolationLevel.Snapshot;
    68         -            case IsolationLevel.Unspecified:
    69         -                return System.Data.IsolationLevel.Unspecified;
           89  +        }
           90  +
           91  +        //
           92  +        // NOTE: When in "strict" mode, throw an exception if the isolation
           93  +        //       level is not recognized; otherwise, fallback to the default
           94  +        //       isolation level specified by the caller.
           95  +        //
           96  +        if (throwOnUnsupported)
           97  +        {
           98  +            throw new InvalidOperationException(
           99  +                String.Format(CultureInfo.InvariantCulture,
          100  +                "unsupported isolation level {0}", isolationLevel));
    70    101           }
    71    102   
    72         -        //
    73         -        // TODO: Perhaps throw an exception here?
    74         -        //
    75         -        return SQLiteConnection.GetFallbackDefaultIsolationLevel();
          103  +        return defaultIsolationLevel;
    76    104       }
    77    105   
    78    106       ///////////////////////////////////////////////////////////////////////////
    79    107   
    80    108       private void Cleanup(SQLiteConnection cnn)
    81    109       {
    82    110           if (_disposeConnection)

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

    30     30       /// <param name="connection">The connection to open a transaction on</param>
    31     31       /// <param name="deferredLock">TRUE to defer the writelock, or FALSE to lock immediately</param>
    32     32       internal SQLiteTransaction(SQLiteConnection connection, bool deferredLock)
    33     33       {
    34     34         _cnn = connection;
    35     35         _version = _cnn._version;
    36     36   
    37         -      _level = (deferredLock == true) ? IsolationLevel.ReadCommitted : IsolationLevel.Serializable;
           37  +      _level = (deferredLock == true) ?
           38  +          SQLiteConnection.DeferredIsolationLevel :
           39  +          SQLiteConnection.ImmediateIsolationLevel;
    38     40   
    39     41         if (_cnn._transactionLevel++ == 0)
    40     42         {
    41     43           try
    42     44           {
    43     45             using (SQLiteCommand cmd = _cnn.CreateCommand())
    44     46             {

Changes to Tests/tkt-56b42d99c1.eagle.

   406    406   } -cleanup {
   407    407     cleanupDb $fileName
   408    408   
   409    409     unset -nocomplain result results errors code sql dataSource id db fileName
   410    410   } -constraints {eagle monoBug28 command.sql compile.DATA SQLite\
   411    411   System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
   412    412   System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 1$}}
          413  +
          414  +###############################################################################
          415  +
          416  +set flags MapIsolationLevels
          417  +
          418  +###############################################################################
          419  +
          420  +runTest {test tkt-56b42d99c1-1.6 {enlisted transaction isolation} -setup {
          421  +  setupDb [set fileName tkt-56b42d99c1-1.6.db]
          422  +} -body {
          423  +  set id [object invoke Interpreter.GetActive NextId]
          424  +  set dataSource [file join [getDatabaseDirectory] $fileName]
          425  +
          426  +  unset -nocomplain results errors
          427  +
          428  +  set code [compileCSharpWith [subst {
          429  +    using System.Data.SQLite;
          430  +    using System.Reflection;
          431  +    using System.Transactions;
          432  +
          433  +    namespace _Dynamic${id}
          434  +    {
          435  +      public static class Test${id}
          436  +      {
          437  +        public static bool TryEnlistInTransaction()
          438  +        {
          439  +          TransactionOptions transactionOptions = new TransactionOptions();
          440  +          transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
          441  +
          442  +          using (TransactionScope transactionScope = new TransactionScope(
          443  +              TransactionScopeOption.Required, transactionOptions))
          444  +          {
          445  +            using (SQLiteConnection connection1 = new SQLiteConnection(
          446  +                "Data Source=${dataSource};[getFlagsProperty $flags]"))
          447  +            {
          448  +              connection1.Open();
          449  +
          450  +              using (SQLiteConnection connection2 = new SQLiteConnection(
          451  +                  "Data Source=${dataSource};[getFlagsProperty $flags]"))
          452  +              {
          453  +                connection2.Open();
          454  +
          455  +                BindingFlags bindingFlags = BindingFlags.Instance |
          456  +                  BindingFlags.NonPublic | BindingFlags.GetField;
          457  +
          458  +                FieldInfo fieldInfo1 = connection1.GetType().GetField(
          459  +                  "_enlistment", bindingFlags);
          460  +
          461  +                object enlistment1 = fieldInfo1.GetValue(connection1);
          462  +                object enlistment2 = fieldInfo1.GetValue(connection2);
          463  +
          464  +                FieldInfo fieldInfo2 = enlistment1.GetType().GetField(
          465  +                  "_transaction", bindingFlags);
          466  +
          467  +                SQLiteTransaction transaction1 =
          468  +                  (SQLiteTransaction)fieldInfo2.GetValue(enlistment1);
          469  +
          470  +                SQLiteTransaction transaction2 =
          471  +                  (SQLiteTransaction)fieldInfo2.GetValue(enlistment2);
          472  +
          473  +                return (transaction1.IsolationLevel ==
          474  +                        transaction2.IsolationLevel);
          475  +              }
          476  +            }
          477  +          }
          478  +        }
          479  +
          480  +        ///////////////////////////////////////////////////////////////////////
          481  +
          482  +        public static void Main()
          483  +        {
          484  +          // do nothing.
          485  +        }
          486  +      }
          487  +    }
          488  +  }] true true true results errors System.Data.SQLite.dll]
          489  +
          490  +  list $code $results \
          491  +      [expr {[info exists errors] ? $errors : ""}] \
          492  +      [expr {$code eq "Ok" ? [catch {
          493  +        object invoke _Dynamic${id}.Test${id} TryEnlistInTransaction
          494  +      } result] : [set result ""]}] $result
          495  +} -cleanup {
          496  +  cleanupDb $fileName
          497  +
          498  +  unset -nocomplain result results errors code dataSource id db fileName
          499  +} -constraints {eagle monoBug28 command.sql compile.DATA SQLite\
          500  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
          501  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 True$}}
          502  +
          503  +###############################################################################
          504  +
          505  +runTest {test tkt-56b42d99c1-1.7 {enlisted transaction isolation} -setup {
          506  +  setupDb [set fileName tkt-56b42d99c1-1.7.db]
          507  +} -body {
          508  +  set id [object invoke Interpreter.GetActive NextId]
          509  +  set dataSource [file join [getDatabaseDirectory] $fileName]
          510  +
          511  +  unset -nocomplain results errors
          512  +
          513  +  set sql(1) { \
          514  +    CREATE TABLE t1(x); \
          515  +    INSERT INTO t1 (x) VALUES(1); \
          516  +  }
          517  +
          518  +  set sql(2) { \
          519  +    SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'; \
          520  +  }
          521  +
          522  +  set code [compileCSharpWith [subst {
          523  +    using System.Data.SQLite;
          524  +    using System.Transactions;
          525  +
          526  +    namespace _Dynamic${id}
          527  +    {
          528  +      public static class Test${id}
          529  +      {
          530  +        public static int Main()
          531  +        {
          532  +          TransactionOptions transactionOptions = new TransactionOptions();
          533  +          transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
          534  +
          535  +          using (TransactionScope transactionScope = new TransactionScope(
          536  +              TransactionScopeOption.Required, transactionOptions))
          537  +          {
          538  +            using (SQLiteConnection connection1 = new SQLiteConnection(
          539  +                "Data Source=${dataSource};[getFlagsProperty $flags]"))
          540  +            {
          541  +              connection1.Open();
          542  +
          543  +              using (SQLiteConnection connection2 = new SQLiteConnection(
          544  +                  "Data Source=${dataSource};[getFlagsProperty $flags]"))
          545  +              {
          546  +                connection2.Open();
          547  +
          548  +                using (SQLiteCommand command1 = connection1.CreateCommand())
          549  +                {
          550  +                  command1.CommandText = "${sql(1)}";
          551  +                  command1.ExecuteNonQuery();
          552  +
          553  +                  using (SQLiteCommand command2 = connection2.CreateCommand())
          554  +                  {
          555  +                    command2.CommandText = "${sql(2)}";
          556  +                    return (int)(long)command2.ExecuteScalar();
          557  +                  }
          558  +                }
          559  +              }
          560  +            }
          561  +          }
          562  +        }
          563  +      }
          564  +    }
          565  +  }] true true true results errors System.Data.SQLite.dll]
          566  +
          567  +  list $code $results \
          568  +      [expr {[info exists errors] ? $errors : ""}] \
          569  +      [expr {$code eq "Ok" ? [catch {
          570  +        object invoke _Dynamic${id}.Test${id} Main
          571  +      } result] : [set result ""]}] $result
          572  +} -cleanup {
          573  +  cleanupDb $fileName
          574  +
          575  +  unset -nocomplain result results errors code sql dataSource id db fileName
          576  +} -constraints {eagle monoBug28 command.sql compile.DATA SQLite\
          577  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
          578  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 0$}}
          579  +
          580  +###############################################################################
          581  +
          582  +runTest {test tkt-56b42d99c1-1.8 {enlisted transaction isolation} -setup {
          583  +  setupDb [set fileName tkt-56b42d99c1-1.8.db]
          584  +} -body {
          585  +  set id [object invoke Interpreter.GetActive NextId]
          586  +  set dataSource [file join [getDatabaseDirectory] $fileName]
          587  +
          588  +  unset -nocomplain results errors
          589  +
          590  +  set sql(1) { \
          591  +    CREATE TABLE t1(x); \
          592  +    INSERT INTO t1 (x) VALUES(1); \
          593  +  }
          594  +
          595  +  set sql(2) { \
          596  +    SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'; \
          597  +  }
          598  +
          599  +  set code [compileCSharpWith [subst {
          600  +    using System.Data.SQLite;
          601  +    using System.Transactions;
          602  +
          603  +    namespace _Dynamic${id}
          604  +    {
          605  +      public static class Test${id}
          606  +      {
          607  +        public static int Main()
          608  +        {
          609  +          TransactionOptions transactionOptions = new TransactionOptions();
          610  +          transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
          611  +
          612  +          using (TransactionScope transactionScope = new TransactionScope(
          613  +              TransactionScopeOption.Required, transactionOptions))
          614  +          {
          615  +            using (SQLiteConnection connection1 = new SQLiteConnection(
          616  +                "Data Source=${dataSource};Enlist=False;[getFlagsProperty $flags]"))
          617  +            {
          618  +              connection1.Open();
          619  +
          620  +              using (SQLiteConnection connection2 = new SQLiteConnection(
          621  +                  "Data Source=${dataSource};Enlist=False;[getFlagsProperty $flags]"))
          622  +              {
          623  +                connection2.Open();
          624  +
          625  +                using (SQLiteCommand command1 = connection1.CreateCommand())
          626  +                {
          627  +                  command1.CommandText = "${sql(1)}";
          628  +                  command1.ExecuteNonQuery();
          629  +
          630  +                  using (SQLiteCommand command2 = connection2.CreateCommand())
          631  +                  {
          632  +                    command2.CommandText = "${sql(2)}";
          633  +                    return (int)(long)command2.ExecuteScalar();
          634  +                  }
          635  +                }
          636  +              }
          637  +            }
          638  +          }
          639  +        }
          640  +      }
          641  +    }
          642  +  }] true true true results errors System.Data.SQLite.dll]
          643  +
          644  +  list $code $results \
          645  +      [expr {[info exists errors] ? $errors : ""}] \
          646  +      [expr {$code eq "Ok" ? [catch {
          647  +        object invoke _Dynamic${id}.Test${id} Main
          648  +      } result] : [set result ""]}] $result
          649  +} -cleanup {
          650  +  cleanupDb $fileName
          651  +
          652  +  unset -nocomplain result results errors code sql dataSource id db fileName
          653  +} -constraints {eagle monoBug28 command.sql compile.DATA SQLite\
          654  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
          655  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 1$}}
          656  +
          657  +###############################################################################
          658  +
          659  +runTest {test tkt-56b42d99c1-1.9 {enlisted transaction isolation} -setup {
          660  +  setupDb [set fileName tkt-56b42d99c1-1.9.db]
          661  +} -body {
          662  +  set id [object invoke Interpreter.GetActive NextId]
          663  +  set dataSource [file join [getDatabaseDirectory] $fileName]
          664  +
          665  +  unset -nocomplain results errors
          666  +
          667  +  set sql(1) { \
          668  +    CREATE TABLE t1(x); \
          669  +    INSERT INTO t1 (x) VALUES(1); \
          670  +  }
          671  +
          672  +  set sql(2) { \
          673  +    SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'; \
          674  +  }
          675  +
          676  +  set code [compileCSharpWith [subst {
          677  +    using System.Data.SQLite;
          678  +    using System.Transactions;
          679  +
          680  +    namespace _Dynamic${id}
          681  +    {
          682  +      public static class Test${id}
          683  +      {
          684  +        public static int Main()
          685  +        {
          686  +          TransactionOptions transactionOptions = new TransactionOptions();
          687  +          transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
          688  +
          689  +          using (TransactionScope transactionScope = new TransactionScope(
          690  +              TransactionScopeOption.Required, transactionOptions))
          691  +          {
          692  +            using (SQLiteConnection connection1 = new SQLiteConnection(
          693  +                "Data Source=${dataSource};[getFlagsProperty $flags]"))
          694  +            {
          695  +              connection1.Open();
          696  +
          697  +              using (SQLiteConnection connection2 = new SQLiteConnection(
          698  +                  "Data Source=${dataSource};Enlist=False;[getFlagsProperty $flags]"))
          699  +              {
          700  +                connection2.Open();
          701  +
          702  +                using (SQLiteCommand command1 = connection1.CreateCommand())
          703  +                {
          704  +                  command1.CommandText = "${sql(1)}";
          705  +                  command1.ExecuteNonQuery();
          706  +
          707  +                  using (SQLiteCommand command2 = connection2.CreateCommand())
          708  +                  {
          709  +                    command2.CommandText = "${sql(2)}";
          710  +                    return (int)(long)command2.ExecuteScalar();
          711  +                  }
          712  +                }
          713  +              }
          714  +            }
          715  +          }
          716  +        }
          717  +      }
          718  +    }
          719  +  }] true true true results errors System.Data.SQLite.dll]
          720  +
          721  +  list $code $results \
          722  +      [expr {[info exists errors] ? $errors : ""}] \
          723  +      [expr {$code eq "Ok" ? [catch {
          724  +        object invoke _Dynamic${id}.Test${id} Main
          725  +      } result] : [set result ""]}] $result
          726  +} -cleanup {
          727  +  cleanupDb $fileName
          728  +
          729  +  unset -nocomplain result results errors code sql dataSource id db fileName
          730  +} -constraints {eagle monoBug28 command.sql compile.DATA SQLite\
          731  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
          732  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 0$}}
          733  +
          734  +###############################################################################
          735  +
          736  +runTest {test tkt-56b42d99c1-1.10 {enlisted transaction isolation} -setup {
          737  +  setupDb [set fileName tkt-56b42d99c1-1.10.db]
          738  +} -body {
          739  +  set id [object invoke Interpreter.GetActive NextId]
          740  +  set dataSource [file join [getDatabaseDirectory] $fileName]
          741  +
          742  +  unset -nocomplain results errors
          743  +
          744  +  set sql(1) { \
          745  +    CREATE TABLE t1(x); \
          746  +    INSERT INTO t1 (x) VALUES(1); \
          747  +  }
          748  +
          749  +  set sql(2) { \
          750  +    SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'; \
          751  +  }
          752  +
          753  +  set code [compileCSharpWith [subst {
          754  +    using System.Data.SQLite;
          755  +    using System.Transactions;
          756  +
          757  +    namespace _Dynamic${id}
          758  +    {
          759  +      public static class Test${id}
          760  +      {
          761  +        public static int Main()
          762  +        {
          763  +          TransactionOptions transactionOptions = new TransactionOptions();
          764  +          transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
          765  +
          766  +          using (TransactionScope transactionScope = new TransactionScope(
          767  +              TransactionScopeOption.Required, transactionOptions))
          768  +          {
          769  +            using (SQLiteConnection connection1 = new SQLiteConnection(
          770  +                "Data Source=${dataSource};Enlist=False;[getFlagsProperty $flags]"))
          771  +            {
          772  +              connection1.Open();
          773  +
          774  +              using (SQLiteConnection connection2 = new SQLiteConnection(
          775  +                  "Data Source=${dataSource};[getFlagsProperty $flags]"))
          776  +              {
          777  +                connection2.Open();
          778  +
          779  +                using (SQLiteCommand command1 = connection1.CreateCommand())
          780  +                {
          781  +                  command1.CommandText = "${sql(1)}";
          782  +                  command1.ExecuteNonQuery();
          783  +
          784  +                  using (SQLiteCommand command2 = connection2.CreateCommand())
          785  +                  {
          786  +                    command2.CommandText = "${sql(2)}";
          787  +                    return (int)(long)command2.ExecuteScalar();
          788  +                  }
          789  +                }
          790  +              }
          791  +            }
          792  +          }
          793  +        }
          794  +      }
          795  +    }
          796  +  }] true true true results errors System.Data.SQLite.dll]
          797  +
          798  +  list $code $results \
          799  +      [expr {[info exists errors] ? $errors : ""}] \
          800  +      [expr {$code eq "Ok" ? [catch {
          801  +        object invoke _Dynamic${id}.Test${id} Main
          802  +      } result] : [set result ""]}] $result
          803  +} -cleanup {
          804  +  cleanupDb $fileName
          805  +
          806  +  unset -nocomplain result results errors code sql dataSource id db fileName
          807  +} -constraints {eagle monoBug28 command.sql compile.DATA SQLite\
          808  +System.Data.SQLite compileCSharp} -match regexp -result {^Ok\
          809  +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 1$}}
          810  +
          811  +###############################################################################
          812  +
          813  +unset -nocomplain flags
   413    814   
   414    815   ###############################################################################
   415    816   
   416    817   runSQLiteTestEpilogue
   417    818   runTestEpilogue