# 2015 December 10 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The focus # of this file is the sqlite3_snapshot_xxx() APIs. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !snapshot {finish_test; return} set testprefix snapshot_fault #------------------------------------------------------------------------- # Check that an sqlite3_snapshot_open() client cannot be tricked into # reading a corrupt snapshot even if a second client fails while # checkpointing the db. # do_faultsim_test 1.0 -prep { faultsim_delete_and_reopen sqlite3 db2 test.db db2 eval { CREATE TABLE t1(a, b UNIQUE, c UNIQUE); INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500)); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500)); BEGIN; SELECT a FROM t1; } set ::snapshot [sqlite3_snapshot_get db2 main] db2 eval COMMIT db2 eval { UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1; INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500)); } } -body { db eval { PRAGMA wal_checkpoint } } -test { db2 eval BEGIN if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} { if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} { error "error is $msg" } } else { set res [db2 eval { SELECT a FROM t1; PRAGMA integrity_check; }] if {$res != "1 2 3 ok"} { error "res is $res" } } sqlite3_snapshot_free $::snapshot } #------------------------------------------------------------------------- # This test is similar to the previous one. Except, after the # "PRAGMA wal_checkpoint" command fails the db is closed and reopened # so as to require wal file recovery. It should not be possible to open # a snapshot that is part of the body of a recovered wal file. # do_faultsim_test 2.0 -prep { faultsim_delete_and_reopen db eval { CREATE TABLE t1(a, b UNIQUE, c UNIQUE); INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500)); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500)); BEGIN; SELECT a FROM t1; } set ::snapshot [sqlite3_snapshot_get db main] db eval COMMIT db eval { UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1; INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500)); } } -body { db eval { PRAGMA wal_checkpoint } } -test { db_save db close db_restore_and_reopen db eval { SELECT * FROM t1 } db eval BEGIN if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} { if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} { error "error is $msg" } } else { # This branch should actually never be taken. But it was useful in # determining whether or not this test was actually working (by # running a modified version of SQLite that allowed snapshots to be # opened following a recovery). error "TEST HAS FAILED" set res [db eval { SELECT a FROM t1; PRAGMA integrity_check; }] if {$res != "1 2 3 ok"} { error "res is $res" } } sqlite3_snapshot_free $::snapshot } #------------------------------------------------------------------------- # Test the handling of faults that occur within sqlite3_snapshot_open(). # do_faultsim_test 3.0 -prep { faultsim_delete_and_reopen db eval { CREATE TABLE t1(a, b UNIQUE, c UNIQUE); INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500)); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500)); BEGIN; SELECT a FROM t1; } set ::snapshot [sqlite3_snapshot_get db main] db eval COMMIT db eval { UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1; INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500)); INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500)); BEGIN; } } -body { if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } { error $msg } } -test { faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM} \ {1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ} if {$testrc==0} { set res [db eval { SELECT a FROM t1; PRAGMA integrity_check; }] if {$res != "1 2 3 ok"} { error "res is $res" } } sqlite3_snapshot_free $::snapshot } finish_test