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: |
79365bcaed517b41e5eccc8af3e1f098 |
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
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