Annotation of embedaddon/sqlite3/test/wal3.test, revision 1.1

1.1     ! misho       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:   forcecopy test.db test2.db
        !            98:   forcecopy test.db-wal test2.db-wal
        !            99:   forcedelete 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:     forcedelete 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:     execsql { CREATE TABLE filler(a,b,c); }
        !           221: 
        !           222:     set ::syncs [list]
        !           223:     T filter xSync
        !           224:     execsql {
        !           225:       CREATE TABLE x(y);
        !           226:       INSERT INTO x VALUES('z');
        !           227:       PRAGMA wal_checkpoint;
        !           228:     }
        !           229:     T filter {}
        !           230:     set ::syncs
        !           231:   } $synccount
        !           232: 
        !           233:   db close
        !           234:   T delete
        !           235: }
        !           236: 
        !           237: #-------------------------------------------------------------------------
        !           238: # When recovering the contents of a WAL file, a process obtains the WRITER
        !           239: # lock, then locks all other bytes before commencing recovery. If it fails
        !           240: # to lock all other bytes (because some other process is holding a read
        !           241: # lock) it should retry up to 100 times. Then return SQLITE_PROTOCOL to the 
        !           242: # caller. Test this (test case wal3-4.3).
        !           243: #
        !           244: # Also test the effect of hitting an SQLITE_BUSY while attempting to obtain
        !           245: # the WRITER lock (should be the same). Test case wal3-4.4.
        !           246: # 
        !           247: proc lock_callback {method filename handle lock} {
        !           248:   lappend ::locks $lock
        !           249: }
        !           250: do_test wal3-4.1 {
        !           251:   testvfs T
        !           252:   T filter xShmLock 
        !           253:   T script lock_callback
        !           254:   set ::locks [list]
        !           255:   sqlite3 db test.db -vfs T
        !           256:   execsql { SELECT * FROM x }
        !           257:   lrange $::locks 0 3
        !           258: } [list {0 1 lock exclusive} {1 7 lock exclusive}      \
        !           259:         {1 7 unlock exclusive} {0 1 unlock exclusive}  \
        !           260: ]
        !           261: do_test wal3-4.2 {
        !           262:   db close
        !           263:   set ::locks [list]
        !           264:   sqlite3 db test.db -vfs T
        !           265:   execsql { SELECT * FROM x }
        !           266:   lrange $::locks 0 3
        !           267: } [list {0 1 lock exclusive} {1 7 lock exclusive}      \
        !           268:         {1 7 unlock exclusive} {0 1 unlock exclusive}  \
        !           269: ]
        !           270: proc lock_callback {method filename handle lock} {
        !           271:   if {$lock == "1 7 lock exclusive"} { return SQLITE_BUSY }
        !           272:   return SQLITE_OK
        !           273: }
        !           274: puts "  Warning: This next test case causes SQLite to call xSleep(1) 100 times."
        !           275: puts "  Normally this equates to a 100ms delay, but if SQLite is built on unix"
        !           276: puts "  without HAVE_USLEEP defined, it may be 100 seconds."
        !           277: do_test wal3-4.3 {
        !           278:   db close
        !           279:   set ::locks [list]
        !           280:   sqlite3 db test.db -vfs T
        !           281:   catchsql { SELECT * FROM x }
        !           282: } {1 {locking protocol}}
        !           283: 
        !           284: puts "  Warning: Same again!"
        !           285: proc lock_callback {method filename handle lock} {
        !           286:   if {$lock == "0 1 lock exclusive"} { return SQLITE_BUSY }
        !           287:   return SQLITE_OK
        !           288: }
        !           289: do_test wal3-4.4 {
        !           290:   db close
        !           291:   set ::locks [list]
        !           292:   sqlite3 db test.db -vfs T
        !           293:   catchsql { SELECT * FROM x }
        !           294: } {1 {locking protocol}}
        !           295: db close
        !           296: T delete
        !           297: 
        !           298: 
        !           299: #-------------------------------------------------------------------------
        !           300: # Only one client may run recovery at a time. Test this mechanism.
        !           301: #
        !           302: # When client-2 tries to open a read transaction while client-1 is 
        !           303: # running recovery, it fails to obtain a lock on an aReadMark[] slot
        !           304: # (because they are all locked by recovery). It then tries to obtain
        !           305: # a shared lock on the RECOVER lock to see if there really is a
        !           306: # recovery running or not.
        !           307: #
        !           308: # This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR
        !           309: # being returned when client-2 attempts a shared lock on the RECOVER byte.
        !           310: #
        !           311: # An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An
        !           312: # SQLITE_IOERR should be returned to the caller.
        !           313: #
        !           314: do_test wal3-5.1 {
        !           315:   faultsim_delete_and_reopen
        !           316:   execsql {
        !           317:     PRAGMA journal_mode = WAL;
        !           318:     CREATE TABLE t1(a, b);
        !           319:     INSERT INTO t1 VALUES(1, 2);
        !           320:     INSERT INTO t1 VALUES(3, 4);
        !           321:   }
        !           322:   faultsim_save_and_close
        !           323: } {}
        !           324: 
        !           325: testvfs T -default 1
        !           326: T script method_callback
        !           327: 
        !           328: proc method_callback {method args} {
        !           329:   if {$method == "xShmBarrier"} {
        !           330:     incr ::barrier_count
        !           331:     if {$::barrier_count == 2} {
        !           332:       # This code is executed within the xShmBarrier() callback invoked
        !           333:       # by the client running recovery as part of writing the recovered
        !           334:       # wal-index header. If a second client attempts to access the 
        !           335:       # database now, it reads a corrupt (partially written) wal-index
        !           336:       # header. But it cannot even get that far, as the first client
        !           337:       # is still holding all the locks (recovery takes an exclusive lock
        !           338:       # on *all* db locks, preventing access by any other client).
        !           339:       #
        !           340:       # If global variable ::wal3_do_lockfailure is non-zero, then set
        !           341:       # things up so that an IO error occurs within an xShmLock() callback
        !           342:       # made by the second client (aka [db2]).
        !           343:       #
        !           344:       sqlite3 db2 test.db
        !           345:       if { $::wal3_do_lockfailure } { T filter xShmLock }
        !           346:       set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ]
        !           347:       T filter {}
        !           348:       db2 close
        !           349:     }
        !           350:   }
        !           351: 
        !           352:   if {$method == "xShmLock"} {
        !           353:     foreach {file handle spec} $args break
        !           354:     if { $spec == "2 1 lock shared" } {
        !           355:       return SQLITE_IOERR
        !           356:     }
        !           357:   }
        !           358: 
        !           359:   return SQLITE_OK
        !           360: }
        !           361: 
        !           362: # Test a normal SQLITE_BUSY return.
        !           363: #
        !           364: T filter xShmBarrier
        !           365: set testrc ""
        !           366: set testmsg ""
        !           367: set barrier_count 0
        !           368: set wal3_do_lockfailure 0
        !           369: do_test wal3-5.2 {
        !           370:   faultsim_restore_and_reopen
        !           371:   execsql { SELECT * FROM t1 }
        !           372: } {1 2 3 4}
        !           373: do_test wal3-5.3 {
        !           374:   list $::testrc $::testmsg
        !           375: } {1 {database is locked}}
        !           376: db close
        !           377: 
        !           378: # Test an SQLITE_IOERR return.
        !           379: #
        !           380: T filter xShmBarrier
        !           381: set barrier_count 0
        !           382: set wal3_do_lockfailure 1
        !           383: set testrc ""
        !           384: set testmsg ""
        !           385: do_test wal3-5.4 {
        !           386:   faultsim_restore_and_reopen
        !           387:   execsql { SELECT * FROM t1 }
        !           388: } {1 2 3 4}
        !           389: do_test wal3-5.5 {
        !           390:   list $::testrc $::testmsg
        !           391: } {1 {disk I/O error}}
        !           392: 
        !           393: db close
        !           394: T delete
        !           395: 
        !           396: #-------------------------------------------------------------------------
        !           397: # When opening a read-transaction on a database, if the entire log has
        !           398: # already been copied to the database file, the reader grabs a special
        !           399: # kind of read lock (on aReadMark[0]). This set of test cases tests the 
        !           400: # outcome of the following:
        !           401: #
        !           402: #   + The reader discovering that between the time when it determined 
        !           403: #     that the log had been completely backfilled and the lock is obtained
        !           404: #     that a writer has written to the log. In this case the reader should
        !           405: #     acquire a different read-lock (not aReadMark[0]) and read the new
        !           406: #     snapshot.
        !           407: #
        !           408: #   + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY.
        !           409: #     This can happen if a checkpoint is ongoing. In this case also simply
        !           410: #     obtain a different read-lock.
        !           411: #
        !           412: catch {db close}
        !           413: testvfs T -default 1
        !           414: do_test wal3-6.1.1 {
        !           415:   forcedelete test.db test.db-journal test.db wal
        !           416:   sqlite3 db test.db
        !           417:   execsql { PRAGMA auto_vacuum = off }
        !           418:   execsql { PRAGMA journal_mode = WAL }
        !           419:   execsql {
        !           420:     CREATE TABLE t1(a, b);
        !           421:     INSERT INTO t1 VALUES('o', 't');
        !           422:     INSERT INTO t1 VALUES('t', 'f');
        !           423:   }
        !           424: } {}
        !           425: do_test wal3-6.1.2 {
        !           426:   sqlite3 db2 test.db
        !           427:   sqlite3 db3 test.db
        !           428:   execsql { BEGIN ; SELECT * FROM t1 } db3
        !           429: } {o t t f}
        !           430: do_test wal3-6.1.3 {
        !           431:   execsql { PRAGMA wal_checkpoint } db2
        !           432: } {0 4 4}
        !           433: 
        !           434: # At this point the log file has been fully checkpointed. However, 
        !           435: # connection [db3] holds a lock that prevents the log from being wrapped.
        !           436: # Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But
        !           437: # as it is obtaining the lock, [db2] appends to the log file.
        !           438: #
        !           439: T filter xShmLock
        !           440: T script lock_callback
        !           441: proc lock_callback {method file handle spec} {
        !           442:   if {$spec == "3 1 lock shared"} {
        !           443:     # This is the callback for [db] to obtain the read lock on aReadMark[0].
        !           444:     # Disable future callbacks using [T filter {}] and write to the log
        !           445:     # file using [db2]. [db3] is preventing [db2] from wrapping the log
        !           446:     # here, so this is an append.
        !           447:     T filter {}
        !           448:     db2 eval { INSERT INTO t1 VALUES('f', 's') }
        !           449:   }
        !           450:   return SQLITE_OK
        !           451: }
        !           452: do_test wal3-6.1.4 {
        !           453:   execsql {
        !           454:     BEGIN;
        !           455:     SELECT * FROM t1;
        !           456:   }
        !           457: } {o t t f f s}
        !           458: 
        !           459: # [db] should be left holding a read-lock on some slot other than 
        !           460: # aReadMark[0]. Test this by demonstrating that the read-lock is preventing
        !           461: # the log from being wrapped.
        !           462: #
        !           463: do_test wal3-6.1.5 {
        !           464:   db3 eval COMMIT
        !           465:   db2 eval { PRAGMA wal_checkpoint }
        !           466:   set sz1 [file size test.db-wal]
        !           467:   db2 eval { INSERT INTO t1 VALUES('s', 'e') }
        !           468:   set sz2 [file size test.db-wal]
        !           469:   expr {$sz2>$sz1}
        !           470: } {1}
        !           471: 
        !           472: # Test that if [db2] had not interfered when [db] was trying to grab
        !           473: # aReadMark[0], it would have been possible to wrap the log in 3.6.1.5.
        !           474: #
        !           475: do_test wal3-6.1.6 {
        !           476:   execsql { COMMIT }
        !           477:   execsql { PRAGMA wal_checkpoint } db2
        !           478:   execsql {
        !           479:     BEGIN;
        !           480:     SELECT * FROM t1;
        !           481:   }
        !           482: } {o t t f f s s e}
        !           483: do_test wal3-6.1.7 {
        !           484:   db2 eval { PRAGMA wal_checkpoint }
        !           485:   set sz1 [file size test.db-wal]
        !           486:   db2 eval { INSERT INTO t1 VALUES('n', 't') }
        !           487:   set sz2 [file size test.db-wal]
        !           488:   expr {$sz2==$sz1}
        !           489: } {1}
        !           490: 
        !           491: db3 close
        !           492: db2 close
        !           493: db close
        !           494: 
        !           495: do_test wal3-6.2.1 {
        !           496:   forcedelete test.db test.db-journal test.db wal
        !           497:   sqlite3 db test.db
        !           498:   sqlite3 db2 test.db
        !           499:   execsql { PRAGMA auto_vacuum = off }
        !           500:   execsql { PRAGMA journal_mode = WAL }
        !           501:   execsql {
        !           502:     CREATE TABLE t1(a, b);
        !           503:     INSERT INTO t1 VALUES('h', 'h');
        !           504:     INSERT INTO t1 VALUES('l', 'b');
        !           505:   }
        !           506: } {}
        !           507: 
        !           508: T filter xShmLock
        !           509: T script lock_callback
        !           510: proc lock_callback {method file handle spec} {
        !           511:   if {$spec == "3 1 unlock exclusive"} {
        !           512:     T filter {}
        !           513:     set ::R [db2 eval {
        !           514:       BEGIN;
        !           515:       SELECT * FROM t1;
        !           516:     }]
        !           517:   }
        !           518: }
        !           519: do_test wal3-6.2.2 {
        !           520:   execsql { PRAGMA wal_checkpoint }
        !           521: } {0 4 4}
        !           522: do_test wal3-6.2.3 {
        !           523:   set ::R
        !           524: } {h h l b}
        !           525: do_test wal3-6.2.4 {
        !           526:   set sz1 [file size test.db-wal]
        !           527:   execsql { INSERT INTO t1 VALUES('b', 'c'); }
        !           528:   set sz2 [file size test.db-wal]
        !           529:   expr {$sz2 > $sz1}
        !           530: } {1}
        !           531: do_test wal3-6.2.5 {
        !           532:   db2 eval { COMMIT }
        !           533:   execsql { PRAGMA wal_checkpoint }
        !           534:   set sz1 [file size test.db-wal]
        !           535:   execsql { INSERT INTO t1 VALUES('n', 'o'); }
        !           536:   set sz2 [file size test.db-wal]
        !           537:   expr {$sz2 == $sz1}
        !           538: } {1}
        !           539:  
        !           540: db2 close
        !           541: db close
        !           542: T delete
        !           543: 
        !           544: #-------------------------------------------------------------------------
        !           545: # When opening a read-transaction on a database, if the entire log has
        !           546: # not yet been copied to the database file, the reader grabs a read
        !           547: # lock on aReadMark[x], where x>0. The following test cases experiment
        !           548: # with the outcome of the following:
        !           549: #
        !           550: #   + The reader discovering that between the time when it read the
        !           551: #     wal-index header and the lock was obtained that a writer has 
        !           552: #     written to the log. In this case the reader should re-read the 
        !           553: #     wal-index header and lock a snapshot corresponding to the new 
        !           554: #     header.
        !           555: #
        !           556: #   + The value in the aReadMark[x] slot has been modified since it was
        !           557: #     read.
        !           558: #
        !           559: catch {db close}
        !           560: testvfs T -default 1
        !           561: do_test wal3-7.1.1 {
        !           562:   forcedelete test.db test.db-journal test.db wal
        !           563:   sqlite3 db test.db
        !           564:   execsql {
        !           565:     PRAGMA journal_mode = WAL;
        !           566:     CREATE TABLE blue(red PRIMARY KEY, green);
        !           567:   }
        !           568: } {wal}
        !           569: 
        !           570: T script method_callback
        !           571: T filter xOpen
        !           572: proc method_callback {method args} {
        !           573:   if {$method == "xOpen"} { return "reader" }
        !           574: }
        !           575: do_test wal3-7.1.2 {
        !           576:   sqlite3 db2 test.db
        !           577:   execsql { SELECT * FROM blue } db2
        !           578: } {}
        !           579: 
        !           580: T filter xShmLock
        !           581: set ::locks [list]
        !           582: proc method_callback {method file handle spec} {
        !           583:   if {$handle != "reader" } { return }
        !           584:   if {$method == "xShmLock"} {
        !           585:     catch { execsql { INSERT INTO blue VALUES(1, 2) } }
        !           586:     catch { execsql { INSERT INTO blue VALUES(3, 4) } }
        !           587:   }
        !           588:   lappend ::locks $spec
        !           589: }
        !           590: do_test wal3-7.1.3 {
        !           591:   execsql { SELECT * FROM blue } db2
        !           592: } {1 2 3 4}
        !           593: do_test wal3-7.1.4 {
        !           594:   set ::locks
        !           595: } {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}}
        !           596: 
        !           597: set ::locks [list]
        !           598: proc method_callback {method file handle spec} {
        !           599:   if {$handle != "reader" } { return }
        !           600:   if {$method == "xShmLock"} {
        !           601:     catch { execsql { INSERT INTO blue VALUES(5, 6) } }
        !           602:   }
        !           603:   lappend ::locks $spec
        !           604: }
        !           605: do_test wal3-7.2.1 {
        !           606:   execsql { SELECT * FROM blue } db2
        !           607: } {1 2 3 4 5 6}
        !           608: do_test wal3-7.2.2 {
        !           609:   set ::locks
        !           610: } {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}}
        !           611: 
        !           612: db close
        !           613: db2 close
        !           614: T delete
        !           615: 
        !           616: #-------------------------------------------------------------------------
        !           617: # 
        !           618: do_test wal3-8.1 {
        !           619:   forcedelete test.db test.db-journal test.db wal
        !           620:   sqlite3 db test.db
        !           621:   sqlite3 db2 test.db
        !           622:   execsql {
        !           623:     PRAGMA auto_vacuum = off;
        !           624:     PRAGMA journal_mode = WAL;
        !           625:     CREATE TABLE b(c);
        !           626:     INSERT INTO b VALUES('Tehran');
        !           627:     INSERT INTO b VALUES('Qom');
        !           628:     INSERT INTO b VALUES('Markazi');
        !           629:     PRAGMA wal_checkpoint;
        !           630:   }
        !           631: } {wal 0 5 5}
        !           632: do_test wal3-8.2 {
        !           633:   execsql { SELECT * FROM b }
        !           634: } {Tehran Qom Markazi}
        !           635: do_test wal3-8.3 {
        !           636:   db eval { SELECT * FROM b } {
        !           637:     db eval { INSERT INTO b VALUES('Qazvin') }
        !           638:     set r [db2 eval { SELECT * FROM b }]
        !           639:     break
        !           640:   }
        !           641:   set r
        !           642: } {Tehran Qom Markazi Qazvin}
        !           643: do_test wal3-8.4 {
        !           644:   execsql {
        !           645:     INSERT INTO b VALUES('Gilan');
        !           646:     INSERT INTO b VALUES('Ardabil');
        !           647:   }
        !           648: } {}
        !           649: db2 close
        !           650: 
        !           651: faultsim_save_and_close
        !           652: testvfs T -default 1
        !           653: faultsim_restore_and_reopen
        !           654: T filter xShmLock
        !           655: T script lock_callback
        !           656: 
        !           657: proc lock_callback {method file handle spec} {
        !           658:   if {$spec == "4 1 unlock exclusive"} {
        !           659:     T filter {}
        !           660:     set ::r [catchsql { SELECT * FROM b } db2]
        !           661:   }
        !           662: }
        !           663: sqlite3 db test.db
        !           664: sqlite3 db2 test.db
        !           665: do_test wal3-8.5 {
        !           666:   execsql { SELECT * FROM b }
        !           667: } {Tehran Qom Markazi Qazvin Gilan Ardabil}
        !           668: do_test wal3-8.6 {
        !           669:   set ::r
        !           670: } {1 {locking protocol}}
        !           671: 
        !           672: db close
        !           673: db2 close
        !           674: 
        !           675: faultsim_restore_and_reopen
        !           676: sqlite3 db2 test.db
        !           677: T filter xShmLock
        !           678: T script lock_callback
        !           679: proc lock_callback {method file handle spec} {
        !           680:   if {$spec == "1 7 unlock exclusive"} {
        !           681:     T filter {}
        !           682:     set ::r [catchsql { SELECT * FROM b } db2]
        !           683:   }
        !           684: }
        !           685: unset ::r
        !           686: do_test wal3-8.5 {
        !           687:   execsql { SELECT * FROM b }
        !           688: } {Tehran Qom Markazi Qazvin Gilan Ardabil}
        !           689: do_test wal3-8.6 {
        !           690:   set ::r
        !           691: } {1 {locking protocol}}
        !           692: 
        !           693: db close
        !           694: db2 close
        !           695: T delete
        !           696: 
        !           697: #-------------------------------------------------------------------------
        !           698: # When a connection opens a read-lock on the database, it searches for
        !           699: # an aReadMark[] slot that is already set to the mxFrame value for the
        !           700: # new transaction. If it cannot find one, it attempts to obtain an 
        !           701: # exclusive lock on an aReadMark[] slot for the purposes of modifying
        !           702: # the value, then drops back to a shared-lock for the duration of the
        !           703: # transaction.
        !           704: #
        !           705: # This test case verifies that if an exclusive lock cannot be obtained
        !           706: # on any aReadMark[] slot (because there are already several readers),
        !           707: # the client takes a shared-lock on a slot without modifying the value
        !           708: # and continues.
        !           709: #
        !           710: set nConn 50
        !           711: if { [string match *BSD $tcl_platform(os)] } { set nConn 25 }
        !           712: do_test wal3-9.0 {
        !           713:   forcedelete test.db test.db-journal test.db wal
        !           714:   sqlite3 db test.db
        !           715:   execsql {
        !           716:     PRAGMA page_size = 1024;
        !           717:     PRAGMA journal_mode = WAL;
        !           718:     CREATE TABLE whoami(x);
        !           719:     INSERT INTO whoami VALUES('nobody');
        !           720:   }
        !           721: } {wal}
        !           722: for {set i 0} {$i < $nConn} {incr i} {
        !           723:   set c db$i
        !           724:   do_test wal3-9.1.$i {
        !           725:     sqlite3 $c test.db
        !           726:     execsql { UPDATE whoami SET x = $c }
        !           727:     execsql {
        !           728:       BEGIN;
        !           729:       SELECT * FROM whoami
        !           730:     } $c
        !           731:   } $c
        !           732: }
        !           733: for {set i 0} {$i < $nConn} {incr i} {
        !           734:   set c db$i
        !           735:   do_test wal3-9.2.$i {
        !           736:     execsql { SELECT * FROM whoami } $c
        !           737:   } $c
        !           738: }
        !           739: 
        !           740: set sz [expr 1024 * (2+$AUTOVACUUM)]
        !           741: do_test wal3-9.3 {
        !           742:   for {set i 0} {$i < ($nConn-1)} {incr i} { db$i close }
        !           743:   execsql { PRAGMA wal_checkpoint } 
        !           744:   byte_is_zero test.db [expr $sz-1024]
        !           745: } {1}
        !           746: do_test wal3-9.4 {
        !           747:   db[expr $nConn-1] close
        !           748:   execsql { PRAGMA wal_checkpoint } 
        !           749:   set sz2 [file size test.db]
        !           750:   byte_is_zero test.db [expr $sz-1024]
        !           751: } {0}
        !           752: 
        !           753: do_multiclient_test tn {
        !           754:   do_test wal3-10.$tn.1 {
        !           755:     sql1 {
        !           756:       PRAGMA page_size = 1024;
        !           757:       CREATE TABLE t1(x);
        !           758:       PRAGMA journal_mode = WAL;
        !           759:       PRAGMA wal_autocheckpoint = 100000;
        !           760:       BEGIN;
        !           761:         INSERT INTO t1 VALUES(randomblob(800));
        !           762:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 2
        !           763:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 4
        !           764:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 8
        !           765:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 16
        !           766:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 32
        !           767:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 64
        !           768:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 128
        !           769:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 256
        !           770:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 512
        !           771:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 1024
        !           772:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 2048
        !           773:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 4096
        !           774:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   -- 8192
        !           775:       COMMIT;
        !           776:       CREATE INDEX i1 ON t1(x);
        !           777:     }
        !           778: 
        !           779:     expr {[file size test.db-wal] > [expr 1032*9000]}
        !           780:   } 1
        !           781: 
        !           782:   do_test wal3-10.$tn.2 {
        !           783:     sql2 {PRAGMA integrity_check}
        !           784:   } {ok}
        !           785: }
        !           786: 
        !           787: finish_test

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>