xref: /sqlite-3.40.0/test/snapshot_fault.test (revision 68d28ea4)
17d159794Sdan# 2015 December 10
27d159794Sdan#
37d159794Sdan# The author disclaims copyright to this source code.  In place of
47d159794Sdan# a legal notice, here is a blessing:
57d159794Sdan#
67d159794Sdan#    May you do good and not evil.
77d159794Sdan#    May you find forgiveness for yourself and forgive others.
87d159794Sdan#    May you share freely, never taking more than you give.
97d159794Sdan#
107d159794Sdan#***********************************************************************
117d159794Sdan# This file implements regression tests for SQLite library. The focus
127d159794Sdan# of this file is the sqlite3_snapshot_xxx() APIs.
137d159794Sdan#
147d159794Sdan
157d159794Sdanset testdir [file dirname $argv0]
167d159794Sdansource $testdir/tester.tcl
17*68d28ea4Sdrhifcapable !snapshot {finish_test; return}
187d159794Sdanset testprefix snapshot_fault
197d159794Sdan
207d159794Sdan#-------------------------------------------------------------------------
217d159794Sdan# Check that an sqlite3_snapshot_open() client cannot be tricked into
227d159794Sdan# reading a corrupt snapshot even if a second client fails while
237d159794Sdan# checkpointing the db.
247d159794Sdan#
257d159794Sdando_faultsim_test 1.0 -prep {
267d159794Sdan  faultsim_delete_and_reopen
277d159794Sdan  sqlite3 db2 test.db
287d159794Sdan  db2 eval {
297d159794Sdan    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
307d159794Sdan    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
317d159794Sdan    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
327d159794Sdan    PRAGMA journal_mode = wal;
337d159794Sdan    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
347d159794Sdan    BEGIN;
357d159794Sdan      SELECT a FROM t1;
367d159794Sdan  }
377d159794Sdan  set ::snapshot [sqlite3_snapshot_get db2 main]
387d159794Sdan  db2 eval COMMIT
397d159794Sdan  db2 eval {
407d159794Sdan    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
417d159794Sdan    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
427d159794Sdan    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
437d159794Sdan    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
447d159794Sdan  }
457d159794Sdan} -body {
467d159794Sdan  db eval { PRAGMA wal_checkpoint }
477d159794Sdan} -test {
487d159794Sdan  db2 eval BEGIN
497d159794Sdan  if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
507d159794Sdan    if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
517d159794Sdan      error "error is $msg"
527d159794Sdan    }
537d159794Sdan  } else {
547d159794Sdan    set res [db2 eval {
557d159794Sdan      SELECT a FROM t1;
567d159794Sdan      PRAGMA integrity_check;
577d159794Sdan    }]
587d159794Sdan    if {$res != "1 2 3 ok"} { error "res is $res" }
597d159794Sdan  }
607d159794Sdan
617d159794Sdan  sqlite3_snapshot_free $::snapshot
627d159794Sdan}
637d159794Sdan
647d159794Sdan#-------------------------------------------------------------------------
657d159794Sdan# This test is similar to the previous one. Except, after the
667d159794Sdan# "PRAGMA wal_checkpoint" command fails the db is closed and reopened
677d159794Sdan# so as to require wal file recovery. It should not be possible to open
687d159794Sdan# a snapshot that is part of the body of a recovered wal file.
697d159794Sdan#
707d159794Sdando_faultsim_test 2.0 -prep {
717d159794Sdan  faultsim_delete_and_reopen
727d159794Sdan  db eval {
737d159794Sdan    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
747d159794Sdan    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
757d159794Sdan    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
767d159794Sdan    PRAGMA journal_mode = wal;
777d159794Sdan    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
787d159794Sdan    BEGIN;
797d159794Sdan      SELECT a FROM t1;
807d159794Sdan  }
817d159794Sdan  set ::snapshot [sqlite3_snapshot_get db main]
827d159794Sdan  db eval COMMIT
837d159794Sdan
847d159794Sdan  db eval {
857d159794Sdan    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
867d159794Sdan    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
877d159794Sdan    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
887d159794Sdan    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
897d159794Sdan  }
907d159794Sdan} -body {
917d159794Sdan  db eval { PRAGMA wal_checkpoint }
927d159794Sdan} -test {
937d159794Sdan
947d159794Sdan  db_save
957d159794Sdan  db close
967d159794Sdan  db_restore_and_reopen
977d159794Sdan  db eval { SELECT * FROM t1 }
987d159794Sdan
997d159794Sdan  db eval BEGIN
1007d159794Sdan  if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
1017d159794Sdan    if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
1027d159794Sdan      error "error is $msg"
1037d159794Sdan    }
1047d159794Sdan  } else {
1057d159794Sdan    # This branch should actually never be taken. But it was useful in
1067d159794Sdan    # determining whether or not this test was actually working (by
1077d159794Sdan    # running a modified version of SQLite that allowed snapshots to be
1087d159794Sdan    # opened following a recovery).
1097d159794Sdan    error "TEST HAS FAILED"
1107d159794Sdan
1117d159794Sdan    set res [db eval {
1127d159794Sdan      SELECT a FROM t1;
1137d159794Sdan      PRAGMA integrity_check;
1147d159794Sdan    }]
1157d159794Sdan    if {$res != "1 2 3 ok"} { error "res is $res" }
1167d159794Sdan  }
1177d159794Sdan
1187d159794Sdan  sqlite3_snapshot_free $::snapshot
1197d159794Sdan}
1207d159794Sdan
1217d159794Sdan#-------------------------------------------------------------------------
1227d159794Sdan# Test the handling of faults that occur within sqlite3_snapshot_open().
1237d159794Sdan#
1247d159794Sdando_faultsim_test 3.0 -prep {
1257d159794Sdan  faultsim_delete_and_reopen
1267d159794Sdan  db eval {
1277d159794Sdan    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
1287d159794Sdan    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
1297d159794Sdan    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
1307d159794Sdan    PRAGMA journal_mode = wal;
1317d159794Sdan    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
1327d159794Sdan    BEGIN;
1337d159794Sdan      SELECT a FROM t1;
1347d159794Sdan  }
1357d159794Sdan  set ::snapshot [sqlite3_snapshot_get db main]
1367d159794Sdan  db eval COMMIT
1377d159794Sdan  db eval {
1387d159794Sdan    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
1397d159794Sdan    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
1407d159794Sdan    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
1417d159794Sdan    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
1427d159794Sdan    BEGIN;
1437d159794Sdan  }
1447d159794Sdan} -body {
1457d159794Sdan  if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } {
1467d159794Sdan    error $msg
1477d159794Sdan  }
1487d159794Sdan} -test {
1497d159794Sdan  faultsim_test_result {0 {}} {1 SQLITE_IOERR} \
1507d159794Sdan                              {1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ}
1517d159794Sdan  if {$testrc==0} {
1527d159794Sdan    set res [db eval {
1537d159794Sdan      SELECT a FROM t1;
1547d159794Sdan      PRAGMA integrity_check;
1557d159794Sdan    }]
1567d159794Sdan    if {$res != "1 2 3 ok"} { error "res is $res" }
1577d159794Sdan  }
1587d159794Sdan
1597d159794Sdan  sqlite3_snapshot_free $::snapshot
1607d159794Sdan}
1617d159794Sdan
1627d159794Sdan
1637d159794Sdan
1647d159794Sdanfinish_test
165