Home | History | Annotate | Download | only in test
      1 # 2010 May 03
      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/malloc_common.tcl
     19 source $testdir/lock_common.tcl
     20 
     21 ifcapable !wal {finish_test ; return }
     22 
     23 #-------------------------------------------------------------------------
     24 # This test case, walfault-1-*, simulates faults while executing a
     25 #
     26 #   PRAGMA journal_mode = WAL;
     27 #
     28 # statement immediately after creating a new database.
     29 #
     30 do_test walfault-1-pre-1 {
     31   faultsim_delete_and_reopen
     32   faultsim_save_and_close
     33 } {}
     34 do_faultsim_test walfault-1 -prep {
     35   faultsim_restore_and_reopen
     36 } -body {
     37   db eval { PRAGMA main.journal_mode = WAL }
     38 } -test {
     39 
     40   faultsim_test_result {0 wal}
     41 
     42   # Test that the connection that encountered an error as part of 
     43   # "PRAGMA journal_mode = WAL" and a new connection use the same
     44   # journal mode when accessing the database.
     45   #
     46   # If "PRAGMA journal_mode" is executed immediately, connection [db] (the 
     47   # one that hit the error in journal_mode="WAL") might return "wal" even 
     48   # if it failed to switch the database to WAL mode. This is not considered 
     49   # a problem. When it tries to read the database, connection [db] correctly 
     50   # recognizes that it is a rollback database and switches back to a 
     51   # rollback compatible journal mode.
     52   #
     53   if {[permutation] != "inmemory_journal"} {
     54     set jm  [db one  {SELECT * FROM sqlite_master ; PRAGMA main.journal_mode}]
     55     sqlite3 db2 test.db
     56     set jm2 [db2 one {SELECT * FROM sqlite_master ; PRAGMA main.journal_mode}]
     57     db2 close
     58   
     59     if { $jm!=$jm2 } { error "Journal modes do not match: $jm $jm2" }
     60     if { $testrc==0 && $jm!="wal" } { error "Journal mode is not WAL" }
     61   }
     62 }
     63 
     64 #--------------------------------------------------------------------------
     65 # Test case walfault-2-* tests fault injection during recovery of a 
     66 # short WAL file (a dozen frames or thereabouts).
     67 #
     68 do_test walfault-2-pre-1 {
     69   sqlite3 db test.db
     70   execsql {
     71     PRAGMA journal_mode = WAL;
     72     BEGIN;
     73       CREATE TABLE x(y, z, UNIQUE(y, z));
     74       INSERT INTO x VALUES(randomblob(100), randomblob(100));
     75     COMMIT;
     76     PRAGMA wal_checkpoint;
     77 
     78     INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
     79     INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
     80     INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
     81   }
     82   execsql {
     83     SELECT count(*) FROM x
     84   }
     85 } {8}
     86 do_test walfault-2-pre-2 {
     87   faultsim_save_and_close
     88   faultsim_restore_and_reopen
     89   execsql { SELECT count(*) FROM x }
     90 } {8}
     91 do_faultsim_test walfault-2 -prep {
     92   faultsim_restore_and_reopen
     93 } -body {
     94   execsql { SELECT count(*) FROM x }
     95 } -test {
     96   faultsim_test_result {0 8}
     97   faultsim_integrity_check
     98 }
     99 
    100 #--------------------------------------------------------------------------
    101 # Test fault injection while writing and checkpointing a small WAL file.
    102 #
    103 do_test walfault-3-pre-1 {
    104   sqlite3 db test.db
    105   execsql {
    106     PRAGMA auto_vacuum = 1;
    107     PRAGMA journal_mode = WAL;
    108     CREATE TABLE abc(a PRIMARY KEY);
    109     INSERT INTO abc VALUES(randomblob(1500));
    110   }
    111   db close
    112   faultsim_save_and_close
    113 } {}
    114 do_faultsim_test walfault-3 -prep {
    115   faultsim_restore_and_reopen
    116 } -body {
    117   db eval {
    118     DELETE FROM abc;
    119     PRAGMA wal_checkpoint;
    120   }
    121   set {} {}
    122 } -test {
    123   faultsim_test_result {0 {}}
    124 }
    125 
    126 
    127 #--------------------------------------------------------------------------
    128 #
    129 if {[permutation] != "inmemory_journal"} {
    130   faultsim_delete_and_reopen
    131   faultsim_save_and_close
    132   do_faultsim_test walfault-4 -prep {
    133     faultsim_restore_and_reopen
    134   } -body {
    135     execsql {
    136       PRAGMA auto_vacuum = 0;
    137       PRAGMA journal_mode = WAL;
    138       CREATE TABLE t1(a PRIMARY KEY, b);
    139       INSERT INTO t1 VALUES('a', 'b');
    140       PRAGMA wal_checkpoint;
    141       SELECT * FROM t1;
    142     }
    143   } -test {
    144     faultsim_test_result {0 {wal 0 7 7 a b}}
    145     faultsim_integrity_check
    146   } 
    147 }
    148 
    149 #--------------------------------------------------------------------------
    150 #
    151 do_test walfault-5-pre-1 {
    152   faultsim_delete_and_reopen
    153   execsql {
    154     PRAGMA page_size = 512;
    155     PRAGMA journal_mode = WAL;
    156   }
    157   faultsim_save_and_close
    158 } {}
    159 do_faultsim_test walfault-5 -faults shmerr* -prep {
    160   faultsim_restore_and_reopen
    161   execsql { PRAGMA wal_autocheckpoint = 0 }
    162   shmfault filter xShmMap
    163 } -body {
    164   execsql {
    165     CREATE TABLE t1(x);
    166     BEGIN;
    167       INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
    168       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
    169       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
    170       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
    171       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
    172       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
    173       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
    174       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
    175       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
    176       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
    177       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
    178       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
    179       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
    180       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
    181       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
    182     COMMIT;
    183     SELECT count(*) FROM t1;
    184   }
    185 } -test {
    186   faultsim_test_result {0 16384}
    187   faultsim_integrity_check
    188 }
    189 
    190 #--------------------------------------------------------------------------
    191 #
    192 do_test walfault-6-pre-1 {
    193   faultsim_delete_and_reopen
    194   execsql {
    195     PRAGMA page_size = 512;
    196     PRAGMA journal_mode = WAL;
    197     PRAGMA wal_autocheckpoint = 0;
    198     CREATE TABLE t1(x);
    199     BEGIN;
    200       INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
    201       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
    202       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
    203       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
    204       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
    205       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
    206       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
    207       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
    208       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
    209       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
    210       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
    211       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
    212       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
    213       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
    214       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
    215     COMMIT;
    216   }
    217   faultsim_save_and_close
    218 } {}
    219 do_faultsim_test walfault-6 -faults shmerr* -prep {
    220   faultsim_restore_and_reopen
    221   shmfault filter xShmMap
    222 } -body {
    223   execsql { SELECT count(*) FROM t1 }
    224 } -test {
    225   faultsim_test_result {0 16384}
    226   faultsim_integrity_check
    227   set n [db one {SELECT count(*) FROM t1}]
    228   if {$n != 16384 && $n != 0} { error "Incorrect number of rows: $n" }
    229 }
    230 
    231 #--------------------------------------------------------------------------
    232 #
    233 do_test walfault-7-pre-1 {
    234   faultsim_delete_and_reopen
    235   execsql {
    236     PRAGMA page_size = 512;
    237     PRAGMA journal_mode = WAL;
    238     PRAGMA wal_autocheckpoint = 0;
    239     CREATE TABLE t1(x);
    240     BEGIN;
    241       INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
    242       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
    243       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
    244     COMMIT;
    245   }
    246   faultsim_save_and_close
    247 } {}
    248 do_faultsim_test walfault-7 -prep {
    249   faultsim_restore_and_reopen
    250 } -body {
    251   execsql { SELECT count(*) FROM t1 }
    252 } -test {
    253   faultsim_test_result {0 4}
    254   set n [db one {SELECT count(*) FROM t1}]
    255   if {$n != 4 && $n != 0} { error "Incorrect number of rows: $n" }
    256 }
    257 
    258 #--------------------------------------------------------------------------
    259 #
    260 do_test walfault-8-pre-1 {
    261   faultsim_delete_and_reopen
    262   execsql {
    263     PRAGMA journal_mode = WAL;
    264     CREATE TABLE abc(a PRIMARY KEY);
    265     INSERT INTO abc VALUES(randomblob(900));
    266   }
    267   faultsim_save_and_close
    268 } {}
    269 do_faultsim_test walfault-8 -prep {
    270   faultsim_restore_and_reopen
    271   execsql { PRAGMA cache_size = 10 }
    272 } -body {
    273   execsql {
    274     BEGIN;
    275       INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
    276       --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
    277       --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
    278       --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
    279     ROLLBACK;
    280     SELECT count(*) FROM abc;
    281   }
    282 } -test {
    283   faultsim_test_result {0 1}
    284 
    285   faultsim_integrity_check
    286   catch { db eval ROLLBACK }
    287   faultsim_integrity_check
    288 
    289   set n [db one {SELECT count(*) FROM abc}]
    290   if {$n != 1} { error "Incorrect number of rows: $n" }
    291 }
    292 
    293 #--------------------------------------------------------------------------
    294 #
    295 do_test walfault-9-pre-1 {
    296   faultsim_delete_and_reopen
    297   execsql {
    298     PRAGMA journal_mode = WAL;
    299     CREATE TABLE abc(a PRIMARY KEY);
    300     INSERT INTO abc VALUES(randomblob(900));
    301   }
    302   faultsim_save_and_close
    303 } {}
    304 do_faultsim_test walfault-9 -prep {
    305   #if {$iFail<73} { set iFail 73 }
    306   #if {$iFail>73} { exit }
    307   
    308   faultsim_restore_and_reopen
    309   execsql { PRAGMA cache_size = 10 }
    310 } -body {
    311   execsql {
    312     BEGIN;
    313       INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
    314       SAVEPOINT spoint;
    315         INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
    316         INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
    317         INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
    318       ROLLBACK TO spoint;
    319     COMMIT;
    320     SELECT count(*) FROM abc;
    321   }
    322 } -test {
    323   faultsim_test_result {0 2}
    324   faultsim_integrity_check
    325 
    326   catch { db eval { ROLLBACK TO spoint } }
    327   catch { db eval { COMMIT } }
    328   set n [db one {SELECT count(*) FROM abc}]
    329   if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" }
    330 }
    331 
    332 do_test walfault-10-pre1 {
    333   faultsim_delete_and_reopen
    334   execsql {
    335     PRAGMA journal_mode = WAL;
    336     PRAGMA wal_autocheckpoint = 0;
    337     CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB);
    338     CREATE INDEX zzzz ON z(zzz);
    339     INSERT INTO z VALUES(NULL, randomblob(800));
    340     INSERT INTO z VALUES(NULL, randomblob(800));
    341     INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    342     INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    343     INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    344     INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    345     INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    346   }
    347   faultsim_save_and_close
    348 } {}
    349 do_faultsim_test walfault-10 -prep {
    350   faultsim_restore_and_reopen
    351   execsql {
    352     PRAGMA cache_size = 10;
    353     BEGIN;
    354       UPDATE z SET zzz = randomblob(799);
    355   }
    356 
    357   set ::stmt [sqlite3_prepare db "SELECT zzz FROM z WHERE zz IN (1, 2, 3)" -1]
    358   sqlite3_step $::stmt
    359 } -body {
    360   execsql { INSERT INTO z VALUES(NULL, NULL) }
    361 } -test {
    362   sqlite3_finalize $::stmt
    363   faultsim_integrity_check
    364 
    365   faultsim_test_result {0 {}}
    366   catch { db eval { ROLLBACK } }
    367   faultsim_integrity_check
    368 
    369   set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}]
    370   if {$n != "64 51200"} { error "Incorrect data: $n" }
    371 }
    372 
    373 #--------------------------------------------------------------------------
    374 # Test fault injection while checkpointing a large WAL file, if the 
    375 # checkpoint is the first operation run after opening the database.
    376 # This means that some of the required wal-index pages are mapped as part of
    377 # the checkpoint process, which means there are a few more opportunities
    378 # for IO errors.
    379 #
    380 # To speed this up, IO errors are only simulated within xShmMap() calls.
    381 #
    382 do_test walfault-11-pre-1 {
    383   sqlite3 db test.db
    384   execsql {
    385     PRAGMA journal_mode = WAL;
    386     PRAGMA wal_autocheckpoint = 0;
    387     BEGIN;
    388       CREATE TABLE abc(a PRIMARY KEY);
    389       INSERT INTO abc VALUES(randomblob(1500));
    390       INSERT INTO abc VALUES(randomblob(1500));
    391       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --    4
    392       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --    8
    393       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   16
    394       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   32
    395       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   64
    396       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  128
    397       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  256
    398       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  512
    399       INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 1024
    400       INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 2048
    401       INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 4096
    402     COMMIT;
    403   }
    404   faultsim_save_and_close
    405 } {}
    406 do_faultsim_test walfault-11 -faults shmerr* -prep {
    407   catch { db2 close }
    408   faultsim_restore_and_reopen
    409   shmfault filter xShmMap
    410 } -body {
    411   db eval { SELECT count(*) FROM abc }
    412   sqlite3 db2 test.db -vfs shmfault
    413   db2 eval { PRAGMA wal_checkpoint }
    414   set {} {}
    415 } -test {
    416   faultsim_test_result {0 {}}
    417 }
    418 
    419 #-------------------------------------------------------------------------
    420 # Test the handling of the various IO/OOM/SHM errors that may occur during 
    421 # a log recovery operation undertaken as part of a call to 
    422 # sqlite3_wal_checkpoint().
    423 # 
    424 do_test walfault-12-pre-1 {
    425   faultsim_delete_and_reopen
    426   execsql {
    427     PRAGMA journal_mode = WAL;
    428     PRAGMA wal_autocheckpoint = 0;
    429     BEGIN;
    430       CREATE TABLE abc(a PRIMARY KEY);
    431       INSERT INTO abc VALUES(randomblob(1500));
    432       INSERT INTO abc VALUES(randomblob(1500));
    433     COMMIT;
    434   }
    435   faultsim_save_and_close
    436 } {}
    437 do_faultsim_test walfault-12 -prep {
    438   if {[info commands shmfault] == ""} {
    439     testvfs shmfault -default true
    440   }
    441   faultsim_restore_and_reopen
    442   db eval { SELECT * FROM sqlite_master }
    443   shmfault shm test.db [string repeat "\000" 40]
    444 } -body {
    445   set rc [sqlite3_wal_checkpoint db]
    446   if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] }
    447 } -test {
    448   db close
    449   faultsim_test_result {0 {}}
    450 }
    451 
    452 #-------------------------------------------------------------------------
    453 # Test simple recovery, reading and writing a database file using a 
    454 # heap-memory wal-index.
    455 # 
    456 do_test walfault-13-pre-1 {
    457   faultsim_delete_and_reopen
    458   execsql {
    459     PRAGMA journal_mode = WAL;
    460     PRAGMA wal_autocheckpoint = 0;
    461     BEGIN;
    462       CREATE TABLE abc(a PRIMARY KEY);
    463       INSERT INTO abc VALUES(randomblob(1500));
    464       INSERT INTO abc VALUES(randomblob(1500));
    465     COMMIT;
    466   }
    467   faultsim_save_and_close
    468   file delete sv_test.db-shm
    469 } {}
    470 
    471 do_faultsim_test walfault-13.1 -prep {
    472   faultsim_restore_and_reopen
    473 } -body {
    474   db eval { PRAGMA locking_mode = exclusive }
    475   db eval { SELECT count(*) FROM abc }
    476 } -test {
    477   faultsim_test_result {0 2}
    478   if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
    479   faultsim_integrity_check
    480 }
    481 
    482 do_faultsim_test walfault-13.2 -prep {
    483   faultsim_restore_and_reopen
    484   db eval { PRAGMA locking_mode = exclusive }
    485 } -body {
    486   db eval { PRAGMA journal_mode = delete }
    487 } -test {
    488   faultsim_test_result {0 delete}
    489   if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
    490   faultsim_integrity_check
    491 }
    492 
    493 do_test walfault-13-pre-2 {
    494   faultsim_delete_and_reopen
    495   execsql {
    496     BEGIN;
    497       CREATE TABLE abc(a PRIMARY KEY);
    498       INSERT INTO abc VALUES(randomblob(1500));
    499       INSERT INTO abc VALUES(randomblob(1500));
    500     COMMIT;
    501   }
    502   faultsim_save_and_close
    503 } {}
    504 
    505 do_faultsim_test walfault-13.3 -prep {
    506   faultsim_restore_and_reopen
    507 } -body {
    508   db eval { 
    509     PRAGMA locking_mode = exclusive;
    510     PRAGMA journal_mode = WAL;
    511     INSERT INTO abc VALUES(randomblob(1500));
    512   }
    513 } -test {
    514   faultsim_test_result {0 {exclusive wal}}
    515   if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
    516   faultsim_integrity_check
    517   set nRow [db eval {SELECT count(*) FROM abc}]
    518   if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" }
    519 }
    520 
    521 #-------------------------------------------------------------------------
    522 # Test fault-handling when wrapping around to the start of a WAL file.
    523 #
    524 do_test walfault-14-pre {
    525   faultsim_delete_and_reopen
    526   execsql {
    527     PRAGMA auto_vacuum = 0;
    528     PRAGMA journal_mode = WAL;
    529     BEGIN;
    530       CREATE TABLE abc(a PRIMARY KEY);
    531       INSERT INTO abc VALUES(randomblob(1500));
    532       INSERT INTO abc VALUES(randomblob(1500));
    533     COMMIT;
    534   }
    535   faultsim_save_and_close
    536 } {}
    537 do_faultsim_test walfault-14 -prep {
    538   faultsim_restore_and_reopen
    539 } -body {
    540   db eval { 
    541     PRAGMA wal_checkpoint = full;
    542     INSERT INTO abc VALUES(randomblob(1500));
    543   }
    544 } -test {
    545   faultsim_test_result {0 {0 10 10}}
    546   faultsim_integrity_check
    547   set nRow [db eval {SELECT count(*) FROM abc}]
    548   if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" }
    549 }
    550 
    551 finish_test
    552