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