xref: /sqlite-3.40.0/test/walfault.test (revision 824f2842)
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
16set testdir [file dirname $argv0]
17source $testdir/tester.tcl
18source $testdir/malloc_common.tcl
19source $testdir/lock_common.tcl
20
21ifcapable !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#
30do_test walfault-1-pre-1 {
31  faultsim_delete_and_reopen
32  faultsim_save_and_close
33} {}
34do_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#
68do_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}
86do_test walfault-2-pre-2 {
87  faultsim_save_and_close
88  faultsim_restore_and_reopen
89  execsql { SELECT count(*) FROM x }
90} {8}
91do_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#
103do_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} {}
114do_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#
128if {[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  }
148}
149
150#--------------------------------------------------------------------------
151#
152do_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} {}
160do_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
189}
190
191#--------------------------------------------------------------------------
192#
193do_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} {}
220do_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" }
230}
231
232#--------------------------------------------------------------------------
233#
234do_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} {}
249do_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" }
257}
258
259#--------------------------------------------------------------------------
260#
261do_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} {}
270do_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}
285
286  faultsim_integrity_check
287  catch { db eval ROLLBACK }
288  faultsim_integrity_check
289
290  set n [db one {SELECT count(*) FROM abc}]
291  if {$n != 1} { error "Incorrect number of rows: $n" }
292}
293
294#--------------------------------------------------------------------------
295#
296do_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} {}
305do_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
326
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" }
331}
332
333do_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} {}
350do_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  }
357
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
365
366  faultsim_test_result {0 {}}
367  catch { db eval { ROLLBACK } }
368  faultsim_integrity_check
369
370  set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}]
371  if {$n != "64 51200"} { error "Incorrect data: $n" }
372}
373
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.
380#
381# To speed this up, IO errors are only simulated within xShmMap() calls.
382#
383do_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} {}
407do_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 {}}
418}
419
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().
424#
425do_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} {}
438do_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 {}}
451}
452
453#-------------------------------------------------------------------------
454# Test simple recovery, reading and writing a database file using a
455# heap-memory wal-index.
456#
457do_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} {}
471
472do_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
481}
482
483do_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
492}
493
494do_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} {}
505
506do_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" }
520}
521
522#-------------------------------------------------------------------------
523# Test fault-handling when wrapping around to the start of a WAL file.
524#
525do_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} {}
538do_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" }
550}
551
552#-------------------------------------------------------------------------
553# Test fault-handling when switching out of exclusive-locking mode.
554#
555do_test walfault-15-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} {}
568do_faultsim_test walfault-15 -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" }
588}
589
590finish_test
591