Convert the implementation of the ".dbstat" dot-command of the command-line
[sqlite.git] / test / walfault.test
blob4e7064d53b1a32c87978e02522b4c98e63a17eae
1 # 2010 May 03
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
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.
16 set testdir [file dirname $argv0]
17 source $testdir/tester.tcl
18 source $testdir/malloc_common.tcl
19 source $testdir/lock_common.tcl
21 ifcapable !wal {finish_test ; return }
23 #-------------------------------------------------------------------------
24 # This test case, walfault-1-*, simulates faults while executing a
26 #   PRAGMA journal_mode = WAL;
28 # statement immediately after creating a new database.
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 {
40   faultsim_test_result {0 wal}
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   }
64 #--------------------------------------------------------------------------
65 # Test case walfault-2-* tests fault injection during recovery of a 
66 # short WAL file (a dozen frames or thereabouts).
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;
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
100 #--------------------------------------------------------------------------
101 # Test fault injection while writing and checkpointing a small WAL file.
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 {}}
126 #--------------------------------------------------------------------------
128 if {[permutation] != "inmemory_journal"} {
129   faultsim_delete_and_reopen
130   faultsim_save_and_close
131   do_faultsim_test walfault-4 -prep {
132     faultsim_restore_and_reopen
133   } -body {
134     execsql {
135       PRAGMA auto_vacuum = 0;
136       PRAGMA journal_mode = WAL;
137       CREATE TABLE t1(a PRIMARY KEY, b);
138       INSERT INTO t1 VALUES('a', 'b');
139       PRAGMA wal_checkpoint;
140       SELECT * FROM t1;
141     }
142   } -test {
143     # Update: The following changed from {0 {wal 0 7 7 a b}} as a result
144     # of PSOW being set by default.
145     faultsim_test_result {0 {wal 0 5 5 a b}}
146     faultsim_integrity_check
147   } 
150 #--------------------------------------------------------------------------
152 do_test walfault-5-pre-1 {
153   faultsim_delete_and_reopen
154   execsql {
155     PRAGMA page_size = 512;
156     PRAGMA journal_mode = WAL;
157   }
158   faultsim_save_and_close
159 } {}
160 do_faultsim_test walfault-5 -faults shmerr* -prep {
161   faultsim_restore_and_reopen
162   execsql { PRAGMA wal_autocheckpoint = 0 }
163   shmfault filter xShmMap
164 } -body {
165   execsql {
166     CREATE TABLE t1(x);
167     BEGIN;
168       INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
169       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
170       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
171       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
172       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
173       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
174       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
175       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
176       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
177       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
178       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
179       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
180       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
181       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
182       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
183     COMMIT;
184     SELECT count(*) FROM t1;
185   }
186 } -test {
187   faultsim_test_result {0 16384}
188   faultsim_integrity_check
191 #--------------------------------------------------------------------------
193 do_test walfault-6-pre-1 {
194   faultsim_delete_and_reopen
195   execsql {
196     PRAGMA page_size = 512;
197     PRAGMA journal_mode = WAL;
198     PRAGMA wal_autocheckpoint = 0;
199     CREATE TABLE t1(x);
200     BEGIN;
201       INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
202       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
203       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
204       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
205       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
206       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
207       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
208       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
209       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
210       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
211       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
212       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
213       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
214       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
215       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
216     COMMIT;
217   }
218   faultsim_save_and_close
219 } {}
220 do_faultsim_test walfault-6 -faults shmerr* -prep {
221   faultsim_restore_and_reopen
222   shmfault filter xShmMap
223 } -body {
224   execsql { SELECT count(*) FROM t1 }
225 } -test {
226   faultsim_test_result {0 16384}
227   faultsim_integrity_check
228   set n [db one {SELECT count(*) FROM t1}]
229   if {$n != 16384 && $n != 0} { error "Incorrect number of rows: $n" }
232 #--------------------------------------------------------------------------
234 do_test walfault-7-pre-1 {
235   faultsim_delete_and_reopen
236   execsql {
237     PRAGMA page_size = 512;
238     PRAGMA journal_mode = WAL;
239     PRAGMA wal_autocheckpoint = 0;
240     CREATE TABLE t1(x);
241     BEGIN;
242       INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
243       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
244       INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
245     COMMIT;
246   }
247   faultsim_save_and_close
248 } {}
249 do_faultsim_test walfault-7 -prep {
250   faultsim_restore_and_reopen
251 } -body {
252   execsql { SELECT count(*) FROM t1 }
253 } -test {
254   faultsim_test_result {0 4}
255   set n [db one {SELECT count(*) FROM t1}]
256   if {$n != 4 && $n != 0} { error "Incorrect number of rows: $n" }
259 #--------------------------------------------------------------------------
261 do_test walfault-8-pre-1 {
262   faultsim_delete_and_reopen
263   execsql {
264     PRAGMA journal_mode = WAL;
265     CREATE TABLE abc(a PRIMARY KEY);
266     INSERT INTO abc VALUES(randomblob(900));
267   }
268   faultsim_save_and_close
269 } {}
270 do_faultsim_test walfault-8 -prep {
271   faultsim_restore_and_reopen
272   execsql { PRAGMA cache_size = 10 }
273 } -body {
274   execsql {
275     BEGIN;
276       INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
277       --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
278       --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
279       --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
280     ROLLBACK;
281     SELECT count(*) FROM abc;
282   }
283 } -test {
284   faultsim_test_result {0 1}
286   faultsim_integrity_check
287   catch { db eval ROLLBACK }
288   faultsim_integrity_check
290   set n [db one {SELECT count(*) FROM abc}]
291   if {$n != 1} { error "Incorrect number of rows: $n" }
294 #--------------------------------------------------------------------------
296 do_test walfault-9-pre-1 {
297   faultsim_delete_and_reopen
298   execsql {
299     PRAGMA journal_mode = WAL;
300     CREATE TABLE abc(a PRIMARY KEY);
301     INSERT INTO abc VALUES(randomblob(900));
302   }
303   faultsim_save_and_close
304 } {}
305 do_faultsim_test walfault-9 -prep {
306   #if {$iFail<73} { set iFail 73 }
307   #if {$iFail>73} { exit }
308   
309   faultsim_restore_and_reopen
310   execsql { PRAGMA cache_size = 10 }
311 } -body {
312   execsql {
313     BEGIN;
314       INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
315       SAVEPOINT spoint;
316         INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
317         INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
318         INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
319       ROLLBACK TO spoint;
320     COMMIT;
321     SELECT count(*) FROM abc;
322   }
323 } -test {
324   faultsim_test_result {0 2}
325   faultsim_integrity_check
327   catch { db eval { ROLLBACK TO spoint } }
328   catch { db eval { COMMIT } }
329   set n [db one {SELECT count(*) FROM abc}]
330   if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" }
333 do_test walfault-10-pre1 {
334   faultsim_delete_and_reopen
335   execsql {
336     PRAGMA journal_mode = WAL;
337     PRAGMA wal_autocheckpoint = 0;
338     CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB);
339     CREATE INDEX zzzz ON z(zzz);
340     INSERT INTO z VALUES(NULL, randomblob(800));
341     INSERT INTO z VALUES(NULL, randomblob(800));
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     INSERT INTO z SELECT NULL, randomblob(800) FROM z;
347   }
348   faultsim_save_and_close
349 } {}
350 do_faultsim_test walfault-10 -prep {
351   faultsim_restore_and_reopen
352   execsql {
353     PRAGMA cache_size = 10;
354     BEGIN;
355       UPDATE z SET zzz = randomblob(799);
356   }
358   set ::stmt [sqlite3_prepare db "SELECT zzz FROM z WHERE zz IN (1, 2, 3)" -1]
359   sqlite3_step $::stmt
360 } -body {
361   execsql { INSERT INTO z VALUES(NULL, NULL) }
362 } -test {
363   sqlite3_finalize $::stmt
364   faultsim_integrity_check
366   faultsim_test_result {0 {}}
367   catch { db eval { ROLLBACK } }
368   faultsim_integrity_check
370   set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}]
371   if {$n != "64 51200"} { error "Incorrect data: $n" }
374 #--------------------------------------------------------------------------
375 # Test fault injection while checkpointing a large WAL file, if the 
376 # checkpoint is the first operation run after opening the database.
377 # This means that some of the required wal-index pages are mapped as part of
378 # the checkpoint process, which means there are a few more opportunities
379 # for IO errors.
381 # To speed this up, IO errors are only simulated within xShmMap() calls.
383 do_test walfault-11-pre-1 {
384   sqlite3 db test.db
385   execsql {
386     PRAGMA journal_mode = WAL;
387     PRAGMA wal_autocheckpoint = 0;
388     BEGIN;
389       CREATE TABLE abc(a PRIMARY KEY);
390       INSERT INTO abc VALUES(randomblob(1500));
391       INSERT INTO abc VALUES(randomblob(1500));
392       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --    4
393       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --    8
394       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   16
395       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   32
396       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   64
397       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  128
398       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  256
399       INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  512
400       INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 1024
401       INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 2048
402       INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 4096
403     COMMIT;
404   }
405   faultsim_save_and_close
406 } {}
407 do_faultsim_test walfault-11 -faults shmerr* -prep {
408   catch { db2 close }
409   faultsim_restore_and_reopen
410   shmfault filter xShmMap
411 } -body {
412   db eval { SELECT count(*) FROM abc }
413   sqlite3 db2 test.db -vfs shmfault
414   db2 eval { PRAGMA wal_checkpoint }
415   set {} {}
416 } -test {
417   faultsim_test_result {0 {}}
420 #-------------------------------------------------------------------------
421 # Test the handling of the various IO/OOM/SHM errors that may occur during 
422 # a log recovery operation undertaken as part of a call to 
423 # sqlite3_wal_checkpoint().
425 do_test walfault-12-pre-1 {
426   faultsim_delete_and_reopen
427   execsql {
428     PRAGMA journal_mode = WAL;
429     PRAGMA wal_autocheckpoint = 0;
430     BEGIN;
431       CREATE TABLE abc(a PRIMARY KEY);
432       INSERT INTO abc VALUES(randomblob(1500));
433       INSERT INTO abc VALUES(randomblob(1500));
434     COMMIT;
435   }
436   faultsim_save_and_close
437 } {}
438 do_faultsim_test walfault-12 -prep {
439   if {[info commands shmfault] == ""} {
440     testvfs shmfault -default true
441   }
442   faultsim_restore_and_reopen
443   db eval { SELECT * FROM sqlite_master }
444   shmfault shm test.db [string repeat "\000" 40]
445 } -body {
446   set rc [sqlite3_wal_checkpoint db]
447   if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] }
448 } -test {
449   db close
450   faultsim_test_result {0 {}}
453 #-------------------------------------------------------------------------
454 # Test simple recovery, reading and writing a database file using a 
455 # heap-memory wal-index.
457 do_test walfault-13-pre-1 {
458   faultsim_delete_and_reopen
459   execsql {
460     PRAGMA journal_mode = WAL;
461     PRAGMA wal_autocheckpoint = 0;
462     BEGIN;
463       CREATE TABLE abc(a PRIMARY KEY);
464       INSERT INTO abc VALUES(randomblob(1500));
465       INSERT INTO abc VALUES(randomblob(1500));
466     COMMIT;
467   }
468   faultsim_save_and_close
469   delete_file sv_test.db-shm
470 } {}
472 do_faultsim_test walfault-13.1 -prep {
473   faultsim_restore_and_reopen
474 } -body {
475   db eval { PRAGMA locking_mode = exclusive }
476   db eval { SELECT count(*) FROM abc }
477 } -test {
478   faultsim_test_result {0 2}
479   if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
480   faultsim_integrity_check
483 do_faultsim_test walfault-13.2 -prep {
484   faultsim_restore_and_reopen
485   db eval { PRAGMA locking_mode = exclusive }
486 } -body {
487   db eval { PRAGMA journal_mode = delete }
488 } -test {
489   faultsim_test_result {0 delete}
490   if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
491   faultsim_integrity_check
494 do_test walfault-13-pre-2 {
495   faultsim_delete_and_reopen
496   execsql {
497     BEGIN;
498       CREATE TABLE abc(a PRIMARY KEY);
499       INSERT INTO abc VALUES(randomblob(1500));
500       INSERT INTO abc VALUES(randomblob(1500));
501     COMMIT;
502   }
503   faultsim_save_and_close
504 } {}
506 do_faultsim_test walfault-13.3 -prep {
507   faultsim_restore_and_reopen
508 } -body {
509   db eval { 
510     PRAGMA locking_mode = exclusive;
511     PRAGMA journal_mode = WAL;
512     INSERT INTO abc VALUES(randomblob(1500));
513   }
514 } -test {
515   faultsim_test_result {0 {exclusive wal}}
516   if {[file exists test.db-shm]} { error "Not using heap-memory mode" }
517   faultsim_integrity_check
518   set nRow [db eval {SELECT count(*) FROM abc}]
519   if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" }
522 #-------------------------------------------------------------------------
523 # Test fault-handling when wrapping around to the start of a WAL file.
525 do_test walfault-14-pre {
526   faultsim_delete_and_reopen
527   execsql {
528     PRAGMA auto_vacuum = 0;
529     PRAGMA journal_mode = WAL;
530     BEGIN;
531       CREATE TABLE abc(a PRIMARY KEY);
532       INSERT INTO abc VALUES(randomblob(1500));
533       INSERT INTO abc VALUES(randomblob(1500));
534     COMMIT;
535   }
536   faultsim_save_and_close
537 } {}
538 do_faultsim_test walfault-14 -prep {
539   faultsim_restore_and_reopen
540 } -body {
541   db eval { 
542     PRAGMA wal_checkpoint = full;
543     INSERT INTO abc VALUES(randomblob(1500));
544   }
545 } -test {
546   faultsim_test_result {0 {0 9 9}}
547   faultsim_integrity_check
548   set nRow [db eval {SELECT count(*) FROM abc}]
549   if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" }
552 #-------------------------------------------------------------------------
553 # Test fault-handling when switching out of exclusive-locking mode.
555 do_test walfault-14-pre {
556   faultsim_delete_and_reopen
557   execsql {
558     PRAGMA auto_vacuum = 0;
559     PRAGMA journal_mode = WAL;
560     BEGIN;
561       CREATE TABLE abc(a PRIMARY KEY);
562       INSERT INTO abc VALUES(randomblob(1500));
563       INSERT INTO abc VALUES(randomblob(1500));
564     COMMIT;
565   }
566   faultsim_save_and_close
567 } {}
568 do_faultsim_test walfault-14 -prep {
569   faultsim_restore_and_reopen
570   execsql {
571     SELECT count(*) FROM abc;
572     PRAGMA locking_mode = exclusive;
573     BEGIN;
574       INSERT INTO abc VALUES(randomblob(1500));
575     COMMIT;
576   }
577 } -body {
578   db eval { 
579     PRAGMA locking_mode = normal;
580     BEGIN;
581       INSERT INTO abc VALUES(randomblob(1500));
582     COMMIT;
583   }
584 } -test {
585   faultsim_integrity_check
586   set nRow [db eval {SELECT count(*) FROM abc}]
587   if {$nRow!=3 && $nRow!=4} { error "Bad db content" }
590 finish_test