xref: /sqlite-3.40.0/test/walfault.test (revision f2fcd075)
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} -test {
122  faultsim_test_result {0 {}}
123}
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 journal_mode = WAL;
136      CREATE TABLE t1(a PRIMARY KEY, b);
137      INSERT INTO t1 VALUES('a', 'b');
138      PRAGMA wal_checkpoint;
139      SELECT * FROM t1;
140    }
141  } -test {
142    faultsim_test_result {0 {wal a b}}
143    faultsim_integrity_check
144  }
145}
146
147#--------------------------------------------------------------------------
148#
149do_test walfault-5-pre-1 {
150  faultsim_delete_and_reopen
151  execsql {
152    PRAGMA page_size = 512;
153    PRAGMA journal_mode = WAL;
154  }
155  faultsim_save_and_close
156} {}
157do_faultsim_test walfault-5 -faults shmerr* -prep {
158  faultsim_restore_and_reopen
159  execsql { PRAGMA wal_autocheckpoint = 0 }
160  shmfault filter xShmMap
161} -body {
162  execsql {
163    CREATE TABLE t1(x);
164    BEGIN;
165      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
166      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
167      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
168      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
169      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
170      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
171      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
172      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
173      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
174      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
175      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
176      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
177      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
178      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
179      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
180    COMMIT;
181    SELECT count(*) FROM t1;
182  }
183} -test {
184  faultsim_test_result {0 16384}
185  faultsim_integrity_check
186}
187
188#--------------------------------------------------------------------------
189#
190do_test walfault-6-pre-1 {
191  faultsim_delete_and_reopen
192  execsql {
193    PRAGMA page_size = 512;
194    PRAGMA journal_mode = WAL;
195    PRAGMA wal_autocheckpoint = 0;
196    CREATE TABLE t1(x);
197    BEGIN;
198      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
199      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
200      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
201      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
202      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
203      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
204      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
205      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
206      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
207      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
208      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
209      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
210      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
211      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
212      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
213    COMMIT;
214  }
215  faultsim_save_and_close
216} {}
217do_faultsim_test walfault-6 -faults shmerr* -prep {
218  faultsim_restore_and_reopen
219  shmfault filter xShmMap
220} -body {
221  execsql { SELECT count(*) FROM t1 }
222} -test {
223  faultsim_test_result {0 16384}
224  faultsim_integrity_check
225  set n [db one {SELECT count(*) FROM t1}]
226  if {$n != 16384 && $n != 0} { error "Incorrect number of rows: $n" }
227}
228
229#--------------------------------------------------------------------------
230#
231do_test walfault-7-pre-1 {
232  faultsim_delete_and_reopen
233  execsql {
234    PRAGMA page_size = 512;
235    PRAGMA journal_mode = WAL;
236    PRAGMA wal_autocheckpoint = 0;
237    CREATE TABLE t1(x);
238    BEGIN;
239      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
240      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
241      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
242    COMMIT;
243  }
244  faultsim_save_and_close
245} {}
246do_faultsim_test walfault-7 -prep {
247  faultsim_restore_and_reopen
248} -body {
249  execsql { SELECT count(*) FROM t1 }
250} -test {
251  faultsim_test_result {0 4}
252  set n [db one {SELECT count(*) FROM t1}]
253  if {$n != 4 && $n != 0} { error "Incorrect number of rows: $n" }
254}
255
256#--------------------------------------------------------------------------
257#
258do_test walfault-8-pre-1 {
259  faultsim_delete_and_reopen
260  execsql {
261    PRAGMA journal_mode = WAL;
262    CREATE TABLE abc(a PRIMARY KEY);
263    INSERT INTO abc VALUES(randomblob(900));
264  }
265  faultsim_save_and_close
266} {}
267do_faultsim_test walfault-8 -prep {
268  faultsim_restore_and_reopen
269  execsql { PRAGMA cache_size = 10 }
270} -body {
271  execsql {
272    BEGIN;
273      INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
274      --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
275      --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
276      --INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
277    ROLLBACK;
278    SELECT count(*) FROM abc;
279  }
280} -test {
281  faultsim_test_result {0 1}
282
283  faultsim_integrity_check
284  catch { db eval ROLLBACK }
285  faultsim_integrity_check
286
287  set n [db one {SELECT count(*) FROM abc}]
288  if {$n != 1} { error "Incorrect number of rows: $n" }
289}
290
291#--------------------------------------------------------------------------
292#
293do_test walfault-9-pre-1 {
294  faultsim_delete_and_reopen
295  execsql {
296    PRAGMA journal_mode = WAL;
297    CREATE TABLE abc(a PRIMARY KEY);
298    INSERT INTO abc VALUES(randomblob(900));
299  }
300  faultsim_save_and_close
301} {}
302do_faultsim_test walfault-9 -prep {
303  #if {$iFail<73} { set iFail 73 }
304  #if {$iFail>73} { exit }
305
306  faultsim_restore_and_reopen
307  execsql { PRAGMA cache_size = 10 }
308} -body {
309  execsql {
310    BEGIN;
311      INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
312      SAVEPOINT spoint;
313        INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
314        INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
315        INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
316      ROLLBACK TO spoint;
317    COMMIT;
318    SELECT count(*) FROM abc;
319  }
320} -test {
321  faultsim_test_result {0 2}
322  faultsim_integrity_check
323
324  catch { db eval { ROLLBACK TO spoint } }
325  catch { db eval { COMMIT } }
326  set n [db one {SELECT count(*) FROM abc}]
327  if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" }
328}
329
330do_test walfault-10-pre1 {
331  faultsim_delete_and_reopen
332  execsql {
333    PRAGMA journal_mode = WAL;
334    PRAGMA wal_autocheckpoint = 0;
335    CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB);
336    CREATE INDEX zzzz ON z(zzz);
337    INSERT INTO z VALUES(NULL, randomblob(800));
338    INSERT INTO z VALUES(NULL, randomblob(800));
339    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
340    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
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  }
345  faultsim_save_and_close
346} {}
347do_faultsim_test walfault-10 -prep {
348  faultsim_restore_and_reopen
349  execsql {
350    PRAGMA cache_size = 10;
351    BEGIN;
352      UPDATE z SET zzz = randomblob(799);
353  }
354
355  set ::stmt [sqlite3_prepare db "SELECT zzz FROM z WHERE zz IN (1, 2, 3)" -1]
356  sqlite3_step $::stmt
357} -body {
358  execsql { INSERT INTO z VALUES(NULL, NULL) }
359} -test {
360  sqlite3_finalize $::stmt
361  faultsim_integrity_check
362
363  faultsim_test_result {0 {}}
364  catch { db eval { ROLLBACK } }
365  faultsim_integrity_check
366
367  set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}]
368  if {$n != "64 51200"} { error "Incorrect data: $n" }
369}
370
371#--------------------------------------------------------------------------
372# Test fault injection while checkpointing a large WAL file, if the
373# checkpoint is the first operation run after opening the database.
374# This means that some of the required wal-index pages are mapped as part of
375# the checkpoint process, which means there are a few more opportunities
376# for IO errors.
377#
378# To speed this up, IO errors are only simulated within xShmMap() calls.
379#
380do_test walfault-11-pre-1 {
381  sqlite3 db test.db
382  execsql {
383    PRAGMA journal_mode = WAL;
384    PRAGMA wal_autocheckpoint = 0;
385    BEGIN;
386      CREATE TABLE abc(a PRIMARY KEY);
387      INSERT INTO abc VALUES(randomblob(1500));
388      INSERT INTO abc VALUES(randomblob(1500));
389      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --    4
390      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --    8
391      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   16
392      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   32
393      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   64
394      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  128
395      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  256
396      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  512
397      INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 1024
398      INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 2048
399      INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 4096
400    COMMIT;
401  }
402  faultsim_save_and_close
403} {}
404do_faultsim_test walfault-11 -faults shmerr* -prep {
405  catch { db2 close }
406  faultsim_restore_and_reopen
407  shmfault filter xShmMap
408} -body {
409  db eval { SELECT count(*) FROM abc }
410  sqlite3 db2 test.db -vfs shmfault
411  db2 eval { PRAGMA wal_checkpoint }
412} -test {
413  faultsim_test_result {0 {}}
414}
415
416#-------------------------------------------------------------------------
417# Test the handling of the various IO/OOM/SHM errors that may occur during
418# a log recovery operation undertaken as part of a call to
419# sqlite3_wal_checkpoint().
420#
421do_test walfault-12-pre-1 {
422  faultsim_delete_and_reopen
423  execsql {
424    PRAGMA journal_mode = WAL;
425    PRAGMA wal_autocheckpoint = 0;
426    BEGIN;
427      CREATE TABLE abc(a PRIMARY KEY);
428      INSERT INTO abc VALUES(randomblob(1500));
429      INSERT INTO abc VALUES(randomblob(1500));
430    COMMIT;
431  }
432  faultsim_save_and_close
433} {}
434do_faultsim_test walfault-12 -prep {
435  if {[info commands shmfault] == ""} {
436    testvfs shmfault -default true
437  }
438  faultsim_restore_and_reopen
439  db eval { SELECT * FROM sqlite_master }
440  shmfault shm test.db [string repeat "\000" 40]
441} -body {
442  set rc [sqlite3_wal_checkpoint db]
443  if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] }
444} -test {
445  db close
446  faultsim_test_result {0 {}}
447}
448
449
450finish_test
451