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