xref: /sqlite-3.40.0/test/snapshot_fault.test (revision dfe4e6bb)
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_BUSY_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_BUSY_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
164finish_test
165