xref: /sqlite-3.40.0/test/snapshot_fault.test (revision 7d159794)
1*7d159794Sdan# 2015 December 10
2*7d159794Sdan#
3*7d159794Sdan# The author disclaims copyright to this source code.  In place of
4*7d159794Sdan# a legal notice, here is a blessing:
5*7d159794Sdan#
6*7d159794Sdan#    May you do good and not evil.
7*7d159794Sdan#    May you find forgiveness for yourself and forgive others.
8*7d159794Sdan#    May you share freely, never taking more than you give.
9*7d159794Sdan#
10*7d159794Sdan#***********************************************************************
11*7d159794Sdan# This file implements regression tests for SQLite library. The focus
12*7d159794Sdan# of this file is the sqlite3_snapshot_xxx() APIs.
13*7d159794Sdan#
14*7d159794Sdan
15*7d159794Sdanset testdir [file dirname $argv0]
16*7d159794Sdansource $testdir/tester.tcl
17*7d159794Sdanset testprefix snapshot_fault
18*7d159794Sdan
19*7d159794Sdan#-------------------------------------------------------------------------
20*7d159794Sdan# Check that an sqlite3_snapshot_open() client cannot be tricked into
21*7d159794Sdan# reading a corrupt snapshot even if a second client fails while
22*7d159794Sdan# checkpointing the db.
23*7d159794Sdan#
24*7d159794Sdando_faultsim_test 1.0 -prep {
25*7d159794Sdan  faultsim_delete_and_reopen
26*7d159794Sdan  sqlite3 db2 test.db
27*7d159794Sdan  db2 eval {
28*7d159794Sdan    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
29*7d159794Sdan    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
30*7d159794Sdan    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
31*7d159794Sdan    PRAGMA journal_mode = wal;
32*7d159794Sdan    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
33*7d159794Sdan    BEGIN;
34*7d159794Sdan      SELECT a FROM t1;
35*7d159794Sdan  }
36*7d159794Sdan  set ::snapshot [sqlite3_snapshot_get db2 main]
37*7d159794Sdan  db2 eval COMMIT
38*7d159794Sdan  db2 eval {
39*7d159794Sdan    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
40*7d159794Sdan    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
41*7d159794Sdan    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
42*7d159794Sdan    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
43*7d159794Sdan  }
44*7d159794Sdan} -body {
45*7d159794Sdan  db eval { PRAGMA wal_checkpoint }
46*7d159794Sdan} -test {
47*7d159794Sdan  db2 eval BEGIN
48*7d159794Sdan  if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
49*7d159794Sdan    if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
50*7d159794Sdan      error "error is $msg"
51*7d159794Sdan    }
52*7d159794Sdan  } else {
53*7d159794Sdan    set res [db2 eval {
54*7d159794Sdan      SELECT a FROM t1;
55*7d159794Sdan      PRAGMA integrity_check;
56*7d159794Sdan    }]
57*7d159794Sdan    if {$res != "1 2 3 ok"} { error "res is $res" }
58*7d159794Sdan  }
59*7d159794Sdan
60*7d159794Sdan  sqlite3_snapshot_free $::snapshot
61*7d159794Sdan}
62*7d159794Sdan
63*7d159794Sdan#-------------------------------------------------------------------------
64*7d159794Sdan# This test is similar to the previous one. Except, after the
65*7d159794Sdan# "PRAGMA wal_checkpoint" command fails the db is closed and reopened
66*7d159794Sdan# so as to require wal file recovery. It should not be possible to open
67*7d159794Sdan# a snapshot that is part of the body of a recovered wal file.
68*7d159794Sdan#
69*7d159794Sdando_faultsim_test 2.0 -prep {
70*7d159794Sdan  faultsim_delete_and_reopen
71*7d159794Sdan  db eval {
72*7d159794Sdan    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
73*7d159794Sdan    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
74*7d159794Sdan    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
75*7d159794Sdan    PRAGMA journal_mode = wal;
76*7d159794Sdan    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
77*7d159794Sdan    BEGIN;
78*7d159794Sdan      SELECT a FROM t1;
79*7d159794Sdan  }
80*7d159794Sdan  set ::snapshot [sqlite3_snapshot_get db main]
81*7d159794Sdan  db eval COMMIT
82*7d159794Sdan
83*7d159794Sdan  db eval {
84*7d159794Sdan    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
85*7d159794Sdan    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
86*7d159794Sdan    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
87*7d159794Sdan    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
88*7d159794Sdan  }
89*7d159794Sdan} -body {
90*7d159794Sdan  db eval { PRAGMA wal_checkpoint }
91*7d159794Sdan} -test {
92*7d159794Sdan
93*7d159794Sdan  db_save
94*7d159794Sdan  db close
95*7d159794Sdan  db_restore_and_reopen
96*7d159794Sdan  db eval { SELECT * FROM t1 }
97*7d159794Sdan
98*7d159794Sdan  db eval BEGIN
99*7d159794Sdan  if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
100*7d159794Sdan    if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
101*7d159794Sdan      error "error is $msg"
102*7d159794Sdan    }
103*7d159794Sdan  } else {
104*7d159794Sdan    # This branch should actually never be taken. But it was useful in
105*7d159794Sdan    # determining whether or not this test was actually working (by
106*7d159794Sdan    # running a modified version of SQLite that allowed snapshots to be
107*7d159794Sdan    # opened following a recovery).
108*7d159794Sdan    error "TEST HAS FAILED"
109*7d159794Sdan
110*7d159794Sdan    set res [db eval {
111*7d159794Sdan      SELECT a FROM t1;
112*7d159794Sdan      PRAGMA integrity_check;
113*7d159794Sdan    }]
114*7d159794Sdan    if {$res != "1 2 3 ok"} { error "res is $res" }
115*7d159794Sdan  }
116*7d159794Sdan
117*7d159794Sdan  sqlite3_snapshot_free $::snapshot
118*7d159794Sdan}
119*7d159794Sdan
120*7d159794Sdan#-------------------------------------------------------------------------
121*7d159794Sdan# Test the handling of faults that occur within sqlite3_snapshot_open().
122*7d159794Sdan#
123*7d159794Sdando_faultsim_test 3.0 -prep {
124*7d159794Sdan  faultsim_delete_and_reopen
125*7d159794Sdan  db eval {
126*7d159794Sdan    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
127*7d159794Sdan    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
128*7d159794Sdan    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
129*7d159794Sdan    PRAGMA journal_mode = wal;
130*7d159794Sdan    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
131*7d159794Sdan    BEGIN;
132*7d159794Sdan      SELECT a FROM t1;
133*7d159794Sdan  }
134*7d159794Sdan  set ::snapshot [sqlite3_snapshot_get db main]
135*7d159794Sdan  db eval COMMIT
136*7d159794Sdan  db eval {
137*7d159794Sdan    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
138*7d159794Sdan    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
139*7d159794Sdan    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
140*7d159794Sdan    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
141*7d159794Sdan    BEGIN;
142*7d159794Sdan  }
143*7d159794Sdan} -body {
144*7d159794Sdan  if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } {
145*7d159794Sdan    error $msg
146*7d159794Sdan  }
147*7d159794Sdan} -test {
148*7d159794Sdan  faultsim_test_result {0 {}} {1 SQLITE_IOERR} \
149*7d159794Sdan                              {1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ}
150*7d159794Sdan  if {$testrc==0} {
151*7d159794Sdan    set res [db eval {
152*7d159794Sdan      SELECT a FROM t1;
153*7d159794Sdan      PRAGMA integrity_check;
154*7d159794Sdan    }]
155*7d159794Sdan    if {$res != "1 2 3 ok"} { error "res is $res" }
156*7d159794Sdan  }
157*7d159794Sdan
158*7d159794Sdan  sqlite3_snapshot_free $::snapshot
159*7d159794Sdan}
160*7d159794Sdan
161*7d159794Sdan
162*7d159794Sdan
163*7d159794Sdanfinish_test
164*7d159794Sdan
165