xref: /sqlite-3.40.0/test/snapshot_fault.test (revision f6febee0)
1# 2015 December 10
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 focus
12# of this file is the sqlite3_snapshot_xxx() APIs.
13#
14
15set testdir [file dirname $argv0]
16source $testdir/tester.tcl
17ifcapable !snapshot {finish_test; return}
18set testprefix snapshot_fault
19
20#-------------------------------------------------------------------------
21# Check that an sqlite3_snapshot_open() client cannot be tricked into
22# reading a corrupt snapshot even if a second client fails while
23# checkpointing the db.
24#
25do_faultsim_test 1.0 -prep {
26  faultsim_delete_and_reopen
27  sqlite3 db2 test.db
28  db2 eval {
29    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
30    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
31    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
32    PRAGMA journal_mode = wal;
33    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
34    BEGIN;
35      SELECT a FROM t1;
36  }
37  set ::snapshot [sqlite3_snapshot_get db2 main]
38  db2 eval COMMIT
39  db2 eval {
40    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
41    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
42    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
43    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
44  }
45} -body {
46  db eval { PRAGMA wal_checkpoint }
47} -test {
48  db2 eval BEGIN
49  if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
50    if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
51      error "error is $msg"
52    }
53  } else {
54    set res [db2 eval {
55      SELECT a FROM t1;
56      PRAGMA integrity_check;
57    }]
58    if {$res != "1 2 3 ok"} { error "res is $res" }
59  }
60
61  sqlite3_snapshot_free $::snapshot
62}
63
64#-------------------------------------------------------------------------
65# This test is similar to the previous one. Except, after the
66# "PRAGMA wal_checkpoint" command fails the db is closed and reopened
67# so as to require wal file recovery. It should not be possible to open
68# a snapshot that is part of the body of a recovered wal file.
69#
70do_faultsim_test 2.0 -prep {
71  faultsim_delete_and_reopen
72  db eval {
73    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
74    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
75    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
76    PRAGMA journal_mode = wal;
77    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
78    BEGIN;
79      SELECT a FROM t1;
80  }
81  set ::snapshot [sqlite3_snapshot_get db main]
82  db eval COMMIT
83
84  db eval {
85    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
86    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
87    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
88    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
89  }
90} -body {
91  db eval { PRAGMA wal_checkpoint }
92} -test {
93
94  db_save
95  db close
96  db_restore_and_reopen
97  db eval { SELECT * FROM t1 }
98
99  db eval BEGIN
100  if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
101    if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
102      error "error is $msg"
103    }
104  } else {
105    # This branch should actually never be taken. But it was useful in
106    # determining whether or not this test was actually working (by
107    # running a modified version of SQLite that allowed snapshots to be
108    # opened following a recovery).
109    error "TEST HAS FAILED"
110
111    set res [db eval {
112      SELECT a FROM t1;
113      PRAGMA integrity_check;
114    }]
115    if {$res != "1 2 3 ok"} { error "res is $res" }
116  }
117
118  sqlite3_snapshot_free $::snapshot
119}
120
121#-------------------------------------------------------------------------
122# Test the handling of faults that occur within sqlite3_snapshot_open().
123#
124do_faultsim_test 3.0 -prep {
125  faultsim_delete_and_reopen
126  db eval {
127    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
128    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
129    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
130    PRAGMA journal_mode = wal;
131    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
132    BEGIN;
133      SELECT a FROM t1;
134  }
135  set ::snapshot [sqlite3_snapshot_get db main]
136  db eval COMMIT
137  db eval {
138    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
139    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
140    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
141    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
142    BEGIN;
143  }
144} -body {
145  if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } {
146    error $msg
147  }
148} -test {
149  faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM} \
150                              {1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ}
151  if {$testrc==0} {
152    set res [db eval {
153      SELECT a FROM t1;
154      PRAGMA integrity_check;
155    }]
156    if {$res != "1 2 3 ok"} { error "res is $res" }
157  }
158
159  sqlite3_snapshot_free $::snapshot
160}
161
162#-------------------------------------------------------------------------
163# Test the handling of faults that occur within sqlite3_snapshot_recover().
164#
165reset_db
166do_execsql_test 4.0 {
167  PRAGMA journal_mode = wal;
168  CREATE TABLE t1(zzz);
169  INSERT INTO t1 VALUES('abc');
170  INSERT INTO t1 VALUES('def');
171} {wal}
172faultsim_save_and_close
173
174do_test 4.0.1 {
175  faultsim_restore_and_reopen
176  db eval { SELECT * FROM sqlite_master }
177  sqlite3_snapshot_recover db main
178} {}
179db close
180
181do_faultsim_test 4.0 -faults oom* -prep {
182  faultsim_restore_and_reopen
183  db eval { SELECT * FROM sqlite_master }
184} -body {
185  sqlite3_snapshot_recover db main
186} -test {
187  faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
188}
189
190# The following test cases contrive to call sqlite3_snapshot_recover()
191# before all pages of the *-shm file have been mapped. This tests an
192# extra branch of error handling logic in snapshot_recover().
193#
194reset_db
195do_execsql_test 4.1.0 {
196  PRAGMA page_size = 512;
197  PRAGMA journal_mode = wal;
198  PRAGMA wal_autocheckpoint = 0;
199  CREATE TABLE t1(zzz);
200  INSERT INTO t1 VALUES(randomblob( 500 * 9500 ));
201  PRAGMA user_version = 211;
202} {wal 0}
203
204do_test 4.1.1 {
205  list [file size test.db-shm] [file size test.db]
206} {98304 512}
207
208faultsim_save_and_close
209do_faultsim_test 4.1 -faults shm* -prep {
210  catch { db2 close }
211  catch { db close }
212  faultsim_restore_and_reopen
213  sqlite3 db2 test.db
214  db2 eval { SELECT * FROM sqlite_master }
215  db eval BEGIN
216  sqlite3_snapshot_get_blob db main
217  db eval COMMIT
218} -body {
219  sqlite3_snapshot_recover db main
220} -test {
221  faultsim_test_result {0 {}} {1 SQLITE_IOERR}
222}
223
224#-------------------------------------------------------------------------
225# Test the handling of faults that occur within sqlite3_snapshot_get().
226#
227reset_db
228do_execsql_test 5.0 {
229  PRAGMA page_size = 512;
230  PRAGMA journal_mode = wal;
231  PRAGMA wal_autocheckpoint = 0;
232  CREATE TABLE t1(zzz);
233  INSERT INTO t1 VALUES(randomblob( 5000 ));
234  PRAGMA user_version = 211;
235} {wal 0}
236faultsim_save_and_close
237
238do_faultsim_test 5 -prep {
239  faultsim_restore_and_reopen
240  execsql { SELECT count(*) FROM sqlite_master }
241  execsql BEGIN
242} -body {
243  sqlite3_snapshot_get_blob db main
244  set {} {}
245} -test {
246  execsql END
247  faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM}
248}
249
250
251finish_test
252