Home | History | Annotate | Download | only in test
      1 # 2010 April 13
      2 #
      3 # The author disclaims copyright to this source code.  In place of
      4 # a legal notice, here is a blessing:
      5 #
      6 #    May you do good and not evil.
      7 #    May you find forgiveness for yourself and forgive others.
      8 #    May you share freely, never taking more than you give.
      9 #
     10 #***********************************************************************
     11 # This file implements regression tests for SQLite library.  The
     12 # focus of this file is testing the operation of the library in
     13 # "PRAGMA journal_mode=WAL" mode.
     14 #
     15 
     16 set testdir [file dirname $argv0]
     17 source $testdir/tester.tcl
     18 source $testdir/lock_common.tcl
     19 source $testdir/wal_common.tcl
     20 source $testdir/malloc_common.tcl
     21 ifcapable !wal {finish_test ; return }
     22 
     23 set a_string_counter 1
     24 proc a_string {n} {
     25   global a_string_counter
     26   incr a_string_counter
     27   string range [string repeat "${a_string_counter}." $n] 1 $n
     28 }
     29 db func a_string a_string
     30 
     31 #-------------------------------------------------------------------------
     32 # When a rollback or savepoint rollback occurs, the client may remove
     33 # elements from one of the hash tables in the wal-index. This block
     34 # of test cases tests that nothing appears to go wrong when this is
     35 # done.
     36 #
     37 do_test wal3-1.0 {
     38   execsql {
     39     PRAGMA cache_size = 2000;
     40     PRAGMA page_size = 1024;
     41     PRAGMA auto_vacuum = off;
     42     PRAGMA synchronous = normal;
     43     PRAGMA journal_mode = WAL;
     44     PRAGMA wal_autocheckpoint = 0;
     45     BEGIN;
     46       CREATE TABLE t1(x);
     47       INSERT INTO t1 VALUES( a_string(800) );                  /*    1 */
     48       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    2 */
     49       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    4 */
     50       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*    8 */
     51       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   16 */
     52       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   32 */
     53       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*   64 */
     54       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  128*/
     55       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  256 */
     56       INSERT INTO t1 SELECT a_string(800) FROM t1;             /*  512 */
     57       INSERT INTO t1 SELECT a_string(800) FROM t1;             /* 1024 */
     58       INSERT INTO t1 SELECT a_string(800) FROM t1;             /* 2048 */
     59       INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 1970;  /* 4018 */
     60     COMMIT;
     61     PRAGMA cache_size = 10;
     62   }
     63   wal_frame_count test.db-wal 1024
     64 } 4056
     65 
     66 for {set i 1} {$i < 50} {incr i} {
     67 
     68   do_test wal3-1.$i.1 {
     69     set str [a_string 800]
     70     execsql { UPDATE t1 SET x = $str WHERE rowid = $i }
     71     lappend L [wal_frame_count test.db-wal 1024]
     72     execsql {
     73       BEGIN;
     74         INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 100;
     75       ROLLBACK;
     76       PRAGMA integrity_check;
     77     }
     78   } {ok}
     79 
     80   # Check that everything looks OK from the point of view of an 
     81   # external connection.
     82   #
     83   sqlite3 db2 test.db
     84   do_test wal3-1.$i.2 {
     85     execsql { SELECT count(*) FROM t1 } db2
     86   } 4018
     87   do_test wal3-1.$i.3 {
     88     execsql { SELECT x FROM t1 WHERE rowid = $i }
     89   } $str
     90   do_test wal3-1.$i.4 {
     91     execsql { PRAGMA integrity_check } db2
     92   } {ok}
     93   db2 close
     94   
     95   # Check that the file-system in its current state can be recovered.
     96   # 
     97   file copy -force test.db test2.db
     98   file copy -force test.db-wal test2.db-wal
     99   file delete -force test2.db-journal
    100   sqlite3 db2 test2.db
    101   do_test wal3-1.$i.5 {
    102     execsql { SELECT count(*) FROM t1 } db2
    103   } 4018
    104   do_test wal3-1.$i.6 {
    105     execsql { SELECT x FROM t1 WHERE rowid = $i }
    106   } $str
    107   do_test wal3-1.$i.7 {
    108     execsql { PRAGMA integrity_check } db2
    109   } {ok}
    110   db2 close
    111 }
    112 
    113 proc byte_is_zero {file offset} {
    114   if {[file size test.db] <= $offset} { return 1 }
    115   expr { [hexio_read $file $offset 1] == "00" }
    116 }
    117 
    118 do_multiclient_test i {
    119 
    120   set testname(1) multiproc
    121   set testname(2) singleproc
    122   set tn $testname($i)
    123 
    124   do_test wal3-2.$tn.1 {
    125     sql1 { 
    126       PRAGMA page_size = 1024;
    127       PRAGMA journal_mode = WAL;
    128     }
    129     sql1 {
    130       CREATE TABLE t1(a, b);
    131       INSERT INTO t1 VALUES(1, 'one');
    132       BEGIN;
    133         SELECT * FROM t1;
    134     }
    135   } {1 one}
    136   do_test wal3-2.$tn.2 {
    137     sql2 {
    138       CREATE TABLE t2(a, b);
    139       INSERT INTO t2 VALUES(2, 'two');
    140       BEGIN;
    141         SELECT * FROM t2;
    142     }
    143   } {2 two}
    144   do_test wal3-2.$tn.3 {
    145     sql3 {
    146       CREATE TABLE t3(a, b);
    147       INSERT INTO t3 VALUES(3, 'three');
    148       BEGIN;
    149         SELECT * FROM t3;
    150     }
    151   } {3 three}
    152 
    153   # Try to checkpoint the database using [db]. It should be possible to
    154   # checkpoint everything except the table added by [db3] (checkpointing
    155   # these frames would clobber the snapshot currently being used by [db2]).
    156   #
    157   # After [db2] has committed, a checkpoint can copy the entire log to the
    158   # database file. Checkpointing after [db3] has committed is therefore a
    159   # no-op, as the entire log has already been backfilled.
    160   #
    161   do_test wal3-2.$tn.4 {
    162     sql1 {
    163       COMMIT;
    164       PRAGMA wal_checkpoint;
    165     }
    166     byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]
    167   } {1}
    168   do_test wal3-2.$tn.5 {
    169     sql2 {
    170       COMMIT;
    171       PRAGMA wal_checkpoint;
    172     }
    173     list [byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]]   \
    174          [byte_is_zero test.db [expr $AUTOVACUUM ? 5*1024 : 4*1024]]
    175   } {0 1}
    176   do_test wal3-2.$tn.6 {
    177     sql3 {
    178       COMMIT;
    179       PRAGMA wal_checkpoint;
    180     }
    181     list [byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]]   \
    182          [byte_is_zero test.db [expr $AUTOVACUUM ? 5*1024 : 4*1024]]
    183   } {0 1}
    184 }
    185 catch {db close}
    186 
    187 #-------------------------------------------------------------------------
    188 # Test that that for the simple test:
    189 #
    190 #   CREATE TABLE x(y);
    191 #   INSERT INTO x VALUES('z');
    192 #   PRAGMA wal_checkpoint;
    193 #
    194 # in WAL mode the xSync method is invoked as expected for each of
    195 # synchronous=off, synchronous=normal and synchronous=full.
    196 #
    197 foreach {tn syncmode synccount} {
    198   1 off     
    199     {}
    200   2 normal  
    201     {test.db-wal normal test.db normal}
    202   3 full    
    203     {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}
    204 } {
    205 
    206   proc sync_counter {args} { 
    207     foreach {method filename id flags} $args break
    208     lappend ::syncs [file tail $filename] $flags
    209   }
    210   do_test wal3-3.$tn {
    211     file delete -force test.db test.db-wal test.db-journal
    212   
    213     testvfs T
    214     T filter {} 
    215     T script sync_counter
    216     sqlite3 db test.db -vfs T
    217   
    218     execsql "PRAGMA synchronous = $syncmode"
    219     execsql { PRAGMA journal_mode = WAL }
    220 
    221     set ::syncs [list]
    222     T filter xSync
    223     execsql {
    224       CREATE TABLE x(y);
    225       INSERT INTO x VALUES('z');
    226       PRAGMA wal_checkpoint;
    227     }
    228     T filter {}
    229     set ::syncs
    230   } $synccount
    231 
    232   db close
    233   T delete
    234 }
    235 
    236 #-------------------------------------------------------------------------
    237 # When recovering the contents of a WAL file, a process obtains the WRITER
    238 # lock, then locks all other bytes before commencing recovery. If it fails
    239 # to lock all other bytes (because some other process is holding a read
    240 # lock) it should retry up to 100 times. Then return SQLITE_PROTOCOL to the 
    241 # caller. Test this (test case wal3-4.3).
    242 #
    243 # Also test the effect of hitting an SQLITE_BUSY while attempting to obtain
    244 # the WRITER lock (should be the same). Test case wal3-4.4.
    245 # 
    246 proc lock_callback {method filename handle lock} {
    247   lappend ::locks $lock
    248 }
    249 do_test wal3-4.1 {
    250   testvfs T
    251   T filter xShmLock 
    252   T script lock_callback
    253   set ::locks [list]
    254   sqlite3 db test.db -vfs T
    255   execsql { SELECT * FROM x }
    256   lrange $::locks 0 3
    257 } [list {0 1 lock exclusive} {1 7 lock exclusive}      \
    258         {1 7 unlock exclusive} {0 1 unlock exclusive}  \
    259 ]
    260 do_test wal3-4.2 {
    261   db close
    262   set ::locks [list]
    263   sqlite3 db test.db -vfs T
    264   execsql { SELECT * FROM x }
    265   lrange $::locks 0 3
    266 } [list {0 1 lock exclusive} {1 7 lock exclusive}      \
    267         {1 7 unlock exclusive} {0 1 unlock exclusive}  \
    268 ]
    269 proc lock_callback {method filename handle lock} {
    270   if {$lock == "1 7 lock exclusive"} { return SQLITE_BUSY }
    271   return SQLITE_OK
    272 }
    273 puts "  Warning: This next test case causes SQLite to call xSleep(1) 100 times."
    274 puts "  Normally this equates to a 100ms delay, but if SQLite is built on unix"
    275 puts "  without HAVE_USLEEP defined, it may be 100 seconds."
    276 do_test wal3-4.3 {
    277   db close
    278   set ::locks [list]
    279   sqlite3 db test.db -vfs T
    280   catchsql { SELECT * FROM x }
    281 } {1 {locking protocol}}
    282 
    283 puts "  Warning: Same again!"
    284 proc lock_callback {method filename handle lock} {
    285   if {$lock == "0 1 lock exclusive"} { return SQLITE_BUSY }
    286   return SQLITE_OK
    287 }
    288 do_test wal3-4.4 {
    289   db close
    290   set ::locks [list]
    291   sqlite3 db test.db -vfs T
    292   catchsql { SELECT * FROM x }
    293 } {1 {locking protocol}}
    294 db close
    295 T delete
    296 
    297 
    298 #-------------------------------------------------------------------------
    299 # Only one client may run recovery at a time. Test this mechanism.
    300 #
    301 # When client-2 tries to open a read transaction while client-1 is 
    302 # running recovery, it fails to obtain a lock on an aReadMark[] slot
    303 # (because they are all locked by recovery). It then tries to obtain
    304 # a shared lock on the RECOVER lock to see if there really is a
    305 # recovery running or not.
    306 #
    307 # This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR
    308 # being returned when client-2 attempts a shared lock on the RECOVER byte.
    309 #
    310 # An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An
    311 # SQLITE_IOERR should be returned to the caller.
    312 #
    313 do_test wal3-5.1 {
    314   faultsim_delete_and_reopen
    315   execsql {
    316     PRAGMA journal_mode = WAL;
    317     CREATE TABLE t1(a, b);
    318     INSERT INTO t1 VALUES(1, 2);
    319     INSERT INTO t1 VALUES(3, 4);
    320   }
    321   faultsim_save_and_close
    322 } {}
    323 
    324 testvfs T -default 1
    325 T script method_callback
    326 
    327 proc method_callback {method args} {
    328   if {$method == "xShmBarrier"} {
    329     incr ::barrier_count
    330     if {$::barrier_count == 2} {
    331       # This code is executed within the xShmBarrier() callback invoked
    332       # by the client running recovery as part of writing the recovered
    333       # wal-index header. If a second client attempts to access the 
    334       # database now, it reads a corrupt (partially written) wal-index
    335       # header. But it cannot even get that far, as the first client
    336       # is still holding all the locks (recovery takes an exclusive lock
    337       # on *all* db locks, preventing access by any other client).
    338       #
    339       # If global variable ::wal3_do_lockfailure is non-zero, then set
    340       # things up so that an IO error occurs within an xShmLock() callback
    341       # made by the second client (aka [db2]).
    342       #
    343       sqlite3 db2 test.db
    344       if { $::wal3_do_lockfailure } { T filter xShmLock }
    345       set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ]
    346       T filter {}
    347       db2 close
    348     }
    349   }
    350 
    351   if {$method == "xShmLock"} {
    352     foreach {file handle spec} $args break
    353     if { $spec == "2 1 lock shared" } {
    354       return SQLITE_IOERR
    355     }
    356   }
    357 
    358   return SQLITE_OK
    359 }
    360 
    361 # Test a normal SQLITE_BUSY return.
    362 #
    363 T filter xShmBarrier
    364 set testrc ""
    365 set testmsg ""
    366 set barrier_count 0
    367 set wal3_do_lockfailure 0
    368 do_test wal3-5.2 {
    369   faultsim_restore_and_reopen
    370   execsql { SELECT * FROM t1 }
    371 } {1 2 3 4}
    372 do_test wal3-5.3 {
    373   list $::testrc $::testmsg
    374 } {1 {database is locked}}
    375 db close
    376 
    377 # Test an SQLITE_IOERR return.
    378 #
    379 T filter xShmBarrier
    380 set barrier_count 0
    381 set wal3_do_lockfailure 1
    382 set testrc ""
    383 set testmsg ""
    384 do_test wal3-5.4 {
    385   faultsim_restore_and_reopen
    386   execsql { SELECT * FROM t1 }
    387 } {1 2 3 4}
    388 do_test wal3-5.5 {
    389   list $::testrc $::testmsg
    390 } {1 {disk I/O error}}
    391 
    392 db close
    393 T delete
    394 
    395 #-------------------------------------------------------------------------
    396 # When opening a read-transaction on a database, if the entire log has
    397 # already been copied to the database file, the reader grabs a special
    398 # kind of read lock (on aReadMark[0]). This set of test cases tests the 
    399 # outcome of the following:
    400 #
    401 #   + The reader discovering that between the time when it determined 
    402 #     that the log had been completely backfilled and the lock is obtained
    403 #     that a writer has written to the log. In this case the reader should
    404 #     acquire a different read-lock (not aReadMark[0]) and read the new
    405 #     snapshot.
    406 #
    407 #   + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY.
    408 #     This can happen if a checkpoint is ongoing. In this case also simply
    409 #     obtain a different read-lock.
    410 #
    411 catch {db close}
    412 testvfs T -default 1
    413 do_test wal3-6.1.1 {
    414   file delete -force test.db test.db-journal test.db wal
    415   sqlite3 db test.db
    416   execsql { PRAGMA auto_vacuum = off }
    417   execsql { PRAGMA journal_mode = WAL }
    418   execsql {
    419     CREATE TABLE t1(a, b);
    420     INSERT INTO t1 VALUES('o', 't');
    421     INSERT INTO t1 VALUES('t', 'f');
    422   }
    423 } {}
    424 do_test wal3-6.1.2 {
    425   sqlite3 db2 test.db
    426   sqlite3 db3 test.db
    427   execsql { BEGIN ; SELECT * FROM t1 } db3
    428 } {o t t f}
    429 do_test wal3-6.1.3 {
    430   execsql { PRAGMA wal_checkpoint } db2
    431 } {0 7 7}
    432 
    433 # At this point the log file has been fully checkpointed. However, 
    434 # connection [db3] holds a lock that prevents the log from being wrapped.
    435 # Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But
    436 # as it is obtaining the lock, [db2] appends to the log file.
    437 #
    438 T filter xShmLock
    439 T script lock_callback
    440 proc lock_callback {method file handle spec} {
    441   if {$spec == "3 1 lock shared"} {
    442     # This is the callback for [db] to obtain the read lock on aReadMark[0].
    443     # Disable future callbacks using [T filter {}] and write to the log
    444     # file using [db2]. [db3] is preventing [db2] from wrapping the log
    445     # here, so this is an append.
    446     T filter {}
    447     db2 eval { INSERT INTO t1 VALUES('f', 's') }
    448   }
    449   return SQLITE_OK
    450 }
    451 do_test wal3-6.1.4 {
    452   execsql {
    453     BEGIN;
    454     SELECT * FROM t1;
    455   }
    456 } {o t t f f s}
    457 
    458 # [db] should be left holding a read-lock on some slot other than 
    459 # aReadMark[0]. Test this by demonstrating that the read-lock is preventing
    460 # the log from being wrapped.
    461 #
    462 do_test wal3-6.1.5 {
    463   db3 eval COMMIT
    464   db2 eval { PRAGMA wal_checkpoint }
    465   set sz1 [file size test.db-wal]
    466   db2 eval { INSERT INTO t1 VALUES('s', 'e') }
    467   set sz2 [file size test.db-wal]
    468   expr {$sz2>$sz1}
    469 } {1}
    470 
    471 # Test that if [db2] had not interfered when [db] was trying to grab
    472 # aReadMark[0], it would have been possible to wrap the log in 3.6.1.5.
    473 #
    474 do_test wal3-6.1.6 {
    475   execsql { COMMIT }
    476   execsql { PRAGMA wal_checkpoint } db2
    477   execsql {
    478     BEGIN;
    479     SELECT * FROM t1;
    480   }
    481 } {o t t f f s s e}
    482 do_test wal3-6.1.7 {
    483   db2 eval { PRAGMA wal_checkpoint }
    484   set sz1 [file size test.db-wal]
    485   db2 eval { INSERT INTO t1 VALUES('n', 't') }
    486   set sz2 [file size test.db-wal]
    487   expr {$sz2==$sz1}
    488 } {1}
    489 
    490 db3 close
    491 db2 close
    492 db close
    493 
    494 do_test wal3-6.2.1 {
    495   file delete -force test.db test.db-journal test.db wal
    496   sqlite3 db test.db
    497   sqlite3 db2 test.db
    498   execsql { PRAGMA auto_vacuum = off }
    499   execsql { PRAGMA journal_mode = WAL }
    500   execsql {
    501     CREATE TABLE t1(a, b);
    502     INSERT INTO t1 VALUES('h', 'h');
    503     INSERT INTO t1 VALUES('l', 'b');
    504   }
    505 } {}
    506 
    507 T filter xShmLock
    508 T script lock_callback
    509 proc lock_callback {method file handle spec} {
    510   if {$spec == "3 1 unlock exclusive"} {
    511     T filter {}
    512     set ::R [db2 eval {
    513       BEGIN;
    514       SELECT * FROM t1;
    515     }]
    516   }
    517 }
    518 do_test wal3-6.2.2 {
    519   execsql { PRAGMA wal_checkpoint }
    520 } {0 7 7}
    521 do_test wal3-6.2.3 {
    522   set ::R
    523 } {h h l b}
    524 do_test wal3-6.2.4 {
    525   set sz1 [file size test.db-wal]
    526   execsql { INSERT INTO t1 VALUES('b', 'c'); }
    527   set sz2 [file size test.db-wal]
    528   expr {$sz2 > $sz1}
    529 } {1}
    530 do_test wal3-6.2.5 {
    531   db2 eval { COMMIT }
    532   execsql { PRAGMA wal_checkpoint }
    533   set sz1 [file size test.db-wal]
    534   execsql { INSERT INTO t1 VALUES('n', 'o'); }
    535   set sz2 [file size test.db-wal]
    536   expr {$sz2 == $sz1}
    537 } {1}
    538  
    539 db2 close
    540 db close
    541 T delete
    542 
    543 #-------------------------------------------------------------------------
    544 # When opening a read-transaction on a database, if the entire log has
    545 # not yet been copied to the database file, the reader grabs a read
    546 # lock on aReadMark[x], where x>0. The following test cases experiment
    547 # with the outcome of the following:
    548 #
    549 #   + The reader discovering that between the time when it read the
    550 #     wal-index header and the lock was obtained that a writer has 
    551 #     written to the log. In this case the reader should re-read the 
    552 #     wal-index header and lock a snapshot corresponding to the new 
    553 #     header.
    554 #
    555 #   + The value in the aReadMark[x] slot has been modified since it was
    556 #     read.
    557 #
    558 catch {db close}
    559 testvfs T -default 1
    560 do_test wal3-7.1.1 {
    561   file delete -force test.db test.db-journal test.db wal
    562   sqlite3 db test.db
    563   execsql {
    564     PRAGMA journal_mode = WAL;
    565     CREATE TABLE blue(red PRIMARY KEY, green);
    566   }
    567 } {wal}
    568 
    569 T script method_callback
    570 T filter xOpen
    571 proc method_callback {method args} {
    572   if {$method == "xOpen"} { return "reader" }
    573 }
    574 do_test wal3-7.1.2 {
    575   sqlite3 db2 test.db
    576   execsql { SELECT * FROM blue } db2
    577 } {}
    578 
    579 T filter xShmLock
    580 set ::locks [list]
    581 proc method_callback {method file handle spec} {
    582   if {$handle != "reader" } { return }
    583   if {$method == "xShmLock"} {
    584     catch { execsql { INSERT INTO blue VALUES(1, 2) } }
    585     catch { execsql { INSERT INTO blue VALUES(3, 4) } }
    586   }
    587   lappend ::locks $spec
    588 }
    589 do_test wal3-7.1.3 {
    590   execsql { SELECT * FROM blue } db2
    591 } {1 2 3 4}
    592 do_test wal3-7.1.4 {
    593   set ::locks
    594 } {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}}
    595 
    596 set ::locks [list]
    597 proc method_callback {method file handle spec} {
    598   if {$handle != "reader" } { return }
    599   if {$method == "xShmLock"} {
    600     catch { execsql { INSERT INTO blue VALUES(5, 6) } }
    601   }
    602   lappend ::locks $spec
    603 }
    604 do_test wal3-7.2.1 {
    605   execsql { SELECT * FROM blue } db2
    606 } {1 2 3 4 5 6}
    607 do_test wal3-7.2.2 {
    608   set ::locks
    609 } {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}}
    610 
    611 db close
    612 db2 close
    613 T delete
    614 
    615 #-------------------------------------------------------------------------
    616 # 
    617 do_test wal3-8.1 {
    618   file delete -force test.db test.db-journal test.db wal
    619   sqlite3 db test.db
    620   sqlite3 db2 test.db
    621   execsql {
    622     PRAGMA auto_vacuum = off;
    623     PRAGMA journal_mode = WAL;
    624     CREATE TABLE b(c);
    625     INSERT INTO b VALUES('Tehran');
    626     INSERT INTO b VALUES('Qom');
    627     INSERT INTO b VALUES('Markazi');
    628     PRAGMA wal_checkpoint;
    629   }
    630 } {wal 0 9 9}
    631 do_test wal3-8.2 {
    632   execsql { SELECT * FROM b }
    633 } {Tehran Qom Markazi}
    634 do_test wal3-8.3 {
    635   db eval { SELECT * FROM b } {
    636     db eval { INSERT INTO b VALUES('Qazvin') }
    637     set r [db2 eval { SELECT * FROM b }]
    638     break
    639   }
    640   set r
    641 } {Tehran Qom Markazi Qazvin}
    642 do_test wal3-8.4 {
    643   execsql {
    644     INSERT INTO b VALUES('Gilan');
    645     INSERT INTO b VALUES('Ardabil');
    646   }
    647 } {}
    648 db2 close
    649 
    650 faultsim_save_and_close
    651 testvfs T -default 1
    652 faultsim_restore_and_reopen
    653 T filter xShmLock
    654 T script lock_callback
    655 
    656 proc lock_callback {method file handle spec} {
    657   if {$spec == "4 1 unlock exclusive"} {
    658     T filter {}
    659     set ::r [catchsql { SELECT * FROM b } db2]
    660   }
    661 }
    662 sqlite3 db test.db
    663 sqlite3 db2 test.db
    664 do_test wal3-8.5 {
    665   execsql { SELECT * FROM b }
    666 } {Tehran Qom Markazi Qazvin Gilan Ardabil}
    667 do_test wal3-8.6 {
    668   set ::r
    669 } {1 {locking protocol}}
    670 
    671 db close
    672 db2 close
    673 
    674 faultsim_restore_and_reopen
    675 sqlite3 db2 test.db
    676 T filter xShmLock
    677 T script lock_callback
    678 proc lock_callback {method file handle spec} {
    679   if {$spec == "1 7 unlock exclusive"} {
    680     T filter {}
    681     set ::r [catchsql { SELECT * FROM b } db2]
    682   }
    683 }
    684 unset ::r
    685 do_test wal3-8.5 {
    686   execsql { SELECT * FROM b }
    687 } {Tehran Qom Markazi Qazvin Gilan Ardabil}
    688 do_test wal3-8.6 {
    689   set ::r
    690 } {1 {locking protocol}}
    691 
    692 db close
    693 db2 close
    694 T delete
    695 
    696 #-------------------------------------------------------------------------
    697 # When a connection opens a read-lock on the database, it searches for
    698 # an aReadMark[] slot that is already set to the mxFrame value for the
    699 # new transaction. If it cannot find one, it attempts to obtain an 
    700 # exclusive lock on an aReadMark[] slot for the purposes of modifying
    701 # the value, then drops back to a shared-lock for the duration of the
    702 # transaction.
    703 #
    704 # This test case verifies that if an exclusive lock cannot be obtained
    705 # on any aReadMark[] slot (because there are already several readers),
    706 # the client takes a shared-lock on a slot without modifying the value
    707 # and continues.
    708 #
    709 set nConn 50
    710 if { [string match *BSD $tcl_platform(os)] } { set nConn 35 }
    711 do_test wal3-9.0 {
    712   file delete -force test.db test.db-journal test.db wal
    713   sqlite3 db test.db
    714   execsql {
    715     PRAGMA page_size = 1024;
    716     PRAGMA journal_mode = WAL;
    717     CREATE TABLE whoami(x);
    718     INSERT INTO whoami VALUES('nobody');
    719   }
    720 } {wal}
    721 for {set i 0} {$i < $nConn} {incr i} {
    722   set c db$i
    723   do_test wal3-9.1.$i {
    724     sqlite3 $c test.db
    725     execsql { UPDATE whoami SET x = $c }
    726     execsql {
    727       BEGIN;
    728       SELECT * FROM whoami
    729     } $c
    730   } $c
    731 }
    732 for {set i 0} {$i < $nConn} {incr i} {
    733   set c db$i
    734   do_test wal3-9.2.$i {
    735     execsql { SELECT * FROM whoami } $c
    736   } $c
    737 }
    738 
    739 set sz [expr 1024 * (2+$AUTOVACUUM)]
    740 do_test wal3-9.3 {
    741   for {set i 0} {$i < ($nConn-1)} {incr i} { db$i close }
    742   execsql { PRAGMA wal_checkpoint } 
    743   byte_is_zero test.db [expr $sz-1024]
    744 } {1}
    745 do_test wal3-9.4 {
    746   db[expr $nConn-1] close
    747   execsql { PRAGMA wal_checkpoint } 
    748   set sz2 [file size test.db]
    749   byte_is_zero test.db [expr $sz-1024]
    750 } {0}
    751 
    752 do_multiclient_test tn {
    753   do_test wal3-10.$tn.1 {
    754     sql1 {
    755       PRAGMA page_size = 1024;
    756       CREATE TABLE t1(x);
    757       PRAGMA journal_mode = WAL;
    758       PRAGMA wal_autocheckpoint = 100000;
    759       BEGIN;
    760         INSERT INTO t1 VALUES(randomblob(800));
    761         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 2
    762         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 4
    763         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 8
    764         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 16
    765         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 32
    766         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 64
    767         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 128
    768         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 256
    769         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 512
    770         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 1024
    771         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 2048
    772         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 4096
    773         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 8192
    774       COMMIT;
    775       CREATE INDEX i1 ON t1(x);
    776     }
    777 
    778     expr {[file size test.db-wal] > [expr 1032*9000]}
    779   } 1
    780 
    781   do_test wal3-10.$tn.2 {
    782     sql2 {PRAGMA integrity_check}
    783   } {ok}
    784 }
    785 
    786 finish_test
    787 
    788