b8fff29c68
SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE if an SQLITE_FCNTL_COMMIT_ATOMIC_WRITE call fails. Also, do not use an atomic transaction to create the initial database. This is because if an error occurs while writing to the db file, any changes to the file-size do not seem to be rolled back automatically. The only time this matters is when the file was 0 bytes in size to start with. FossilOrigin-Name: b3122db1545aeb48b7c28d480534b4b0fe04e83d5336225714c3cad926e5960e
1554 lines
42 KiB
Plaintext
1554 lines
42 KiB
Plaintext
# 2010 June 15
|
|
#
|
|
# 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.
|
|
#
|
|
#***********************************************************************
|
|
#
|
|
|
|
set testdir [file dirname $argv0]
|
|
source $testdir/tester.tcl
|
|
source $testdir/lock_common.tcl
|
|
source $testdir/malloc_common.tcl
|
|
|
|
if {[permutation] == "inmemory_journal"} {
|
|
finish_test
|
|
return
|
|
}
|
|
|
|
if {$::tcl_platform(platform)=="windows"} {
|
|
finish_test
|
|
return
|
|
}
|
|
|
|
set a_string_counter 1
|
|
proc a_string {n} {
|
|
global a_string_counter
|
|
incr a_string_counter
|
|
string range [string repeat "${a_string_counter}." $n] 1 $n
|
|
}
|
|
db func a_string a_string
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault-injection while rolling back a hot-journal file.
|
|
#
|
|
do_test pagerfault-1-pre1 {
|
|
execsql {
|
|
PRAGMA journal_mode = DELETE;
|
|
PRAGMA cache_size = 10;
|
|
CREATE TABLE t1(a UNIQUE, b UNIQUE);
|
|
INSERT INTO t1 VALUES(a_string(200), a_string(300));
|
|
INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
|
|
BEGIN;
|
|
INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-1 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql { SELECT count(*) FROM t1 }
|
|
} -test {
|
|
faultsim_test_result {0 4}
|
|
faultsim_integrity_check
|
|
if {[db one { SELECT count(*) FROM t1 }] != 4} {
|
|
error "Database content appears incorrect"
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault-injection while rolling back a hot-journal file with a
|
|
# page-size different from the current value stored on page 1 of the
|
|
# database file.
|
|
#
|
|
do_test pagerfault-2-pre1 {
|
|
testvfs tv -default 1
|
|
tv filter xSync
|
|
tv script xSyncCb
|
|
proc xSyncCb {filename args} {
|
|
if {[string match *journal filename]==0} faultsim_save
|
|
}
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA page_size = 4096;
|
|
BEGIN;
|
|
CREATE TABLE abc(a, b, c);
|
|
INSERT INTO abc VALUES('o', 't', 't');
|
|
INSERT INTO abc VALUES('f', 'f', 's');
|
|
INSERT INTO abc SELECT * FROM abc; -- 4
|
|
INSERT INTO abc SELECT * FROM abc; -- 8
|
|
INSERT INTO abc SELECT * FROM abc; -- 16
|
|
INSERT INTO abc SELECT * FROM abc; -- 32
|
|
INSERT INTO abc SELECT * FROM abc; -- 64
|
|
INSERT INTO abc SELECT * FROM abc; -- 128
|
|
INSERT INTO abc SELECT * FROM abc; -- 256
|
|
COMMIT;
|
|
PRAGMA page_size = 1024;
|
|
VACUUM;
|
|
}
|
|
db close
|
|
tv delete
|
|
} {}
|
|
do_faultsim_test pagerfault-2 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql { SELECT * FROM abc }
|
|
} -test {
|
|
set answer [split [string repeat "ottffs" 128] ""]
|
|
faultsim_test_result [list 0 $answer]
|
|
faultsim_integrity_check
|
|
set res [db eval { SELECT * FROM abc }]
|
|
if {$res != $answer} { error "Database content appears incorrect ($res)" }
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault-injection while rolling back hot-journals that were created
|
|
# as part of a multi-file transaction.
|
|
#
|
|
do_test pagerfault-3-pre1 {
|
|
testvfs tstvfs -default 1
|
|
tstvfs filter xDelete
|
|
tstvfs script xDeleteCallback
|
|
|
|
proc xDeleteCallback {method file args} {
|
|
set file [file tail $file]
|
|
if { [string match *mj* $file] } { faultsim_save }
|
|
}
|
|
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string
|
|
|
|
execsql {
|
|
ATTACH 'test.db2' AS aux;
|
|
PRAGMA journal_mode = DELETE;
|
|
PRAGMA main.cache_size = 10;
|
|
PRAGMA aux.cache_size = 10;
|
|
|
|
CREATE TABLE t1(a UNIQUE, b UNIQUE);
|
|
CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
|
|
INSERT INTO t1 VALUES(a_string(200), a_string(300));
|
|
INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
|
|
INSERT INTO t2 SELECT * FROM t1;
|
|
|
|
BEGIN;
|
|
INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
|
|
REPLACE INTO t2 SELECT * FROM t1;
|
|
COMMIT;
|
|
}
|
|
|
|
db close
|
|
tstvfs delete
|
|
} {}
|
|
do_faultsim_test pagerfault-3 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql {
|
|
ATTACH 'test.db2' AS aux;
|
|
SELECT count(*) FROM t2;
|
|
SELECT count(*) FROM t1;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}}
|
|
faultsim_integrity_check
|
|
catchsql { ATTACH 'test.db2' AS aux }
|
|
if {[db one { SELECT count(*) FROM t1 }] != 4
|
|
|| [db one { SELECT count(*) FROM t2 }] != 4
|
|
} {
|
|
error "Database content appears incorrect"
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault-injection as part of a vanilla, no-transaction, INSERT
|
|
# statement.
|
|
#
|
|
do_faultsim_test pagerfault-4 -prep {
|
|
faultsim_delete_and_reopen
|
|
} -body {
|
|
execsql {
|
|
CREATE TABLE x(y);
|
|
INSERT INTO x VALUES('z');
|
|
SELECT * FROM x;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 z}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault-injection as part of a commit when using journal_mode=PERSIST.
|
|
# Three different cases:
|
|
#
|
|
# pagerfault-5.1: With no journal_size_limit configured.
|
|
# pagerfault-5.2: With a journal_size_limit configured.
|
|
# pagerfault-5.4: Multi-file transaction. One connection has a
|
|
# journal_size_limit of 0, the other has no limit.
|
|
#
|
|
do_test pagerfault-5-pre1 {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
CREATE TABLE t1(a UNIQUE, b UNIQUE);
|
|
INSERT INTO t1 VALUES(a_string(200), a_string(300));
|
|
INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-5.1 -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string
|
|
execsql { PRAGMA journal_mode = PERSIST }
|
|
} -body {
|
|
execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
do_faultsim_test pagerfault-5.2 -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA journal_mode = PERSIST;
|
|
PRAGMA journal_size_limit = 2048;
|
|
}
|
|
} -body {
|
|
execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
do_faultsim_test pagerfault-5.3 -faults oom-transient -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string
|
|
forcedelete test2.db test2.db-journal test2.db-wal
|
|
execsql {
|
|
PRAGMA journal_mode = PERSIST;
|
|
ATTACH 'test2.db' AS aux;
|
|
PRAGMA aux.journal_mode = PERSIST;
|
|
PRAGMA aux.journal_size_limit = 0;
|
|
}
|
|
} -body {
|
|
execsql {
|
|
BEGIN;
|
|
INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
|
|
CREATE TABLE aux.t2 AS SELECT * FROM t1;
|
|
COMMIT;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
|
|
catchsql { COMMIT }
|
|
catchsql { ROLLBACK }
|
|
|
|
faultsim_integrity_check
|
|
set res ""
|
|
set rc [catch { set res [db one { PRAGMA aux.integrity_check }] }]
|
|
if {$rc!=0 || $res != "ok"} {error "integrity-check problem:$rc $res"}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault-injection as part of a commit when using
|
|
# journal_mode=TRUNCATE.
|
|
#
|
|
do_test pagerfault-6-pre1 {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
CREATE TABLE t1(a UNIQUE, b UNIQUE);
|
|
INSERT INTO t1 VALUES(a_string(200), a_string(300));
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
|
|
do_faultsim_test pagerfault-6.1 -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string
|
|
execsql { PRAGMA journal_mode = TRUNCATE }
|
|
} -body {
|
|
execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
|
|
execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
# The unix vfs xAccess() method considers a file zero bytes in size to
|
|
# "not exist". This proc overrides that behaviour so that a zero length
|
|
# file is considered to exist.
|
|
#
|
|
proc xAccess {method filename op args} {
|
|
if {$op != "SQLITE_ACCESS_EXISTS"} { return "" }
|
|
return [file exists $filename]
|
|
}
|
|
do_faultsim_test pagerfault-6.2 -faults cantopen-* -prep {
|
|
shmfault filter xAccess
|
|
shmfault script xAccess
|
|
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string
|
|
execsql { PRAGMA journal_mode = TRUNCATE }
|
|
} -body {
|
|
execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
|
|
execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
# The following was an attempt to get a bitvec malloc to fail. Didn't work.
|
|
#
|
|
# do_test pagerfault-6-pre1 {
|
|
# faultsim_delete_and_reopen
|
|
# execsql {
|
|
# CREATE TABLE t1(x, y, UNIQUE(x, y));
|
|
# INSERT INTO t1 VALUES(1, randomblob(1501));
|
|
# INSERT INTO t1 VALUES(2, randomblob(1502));
|
|
# INSERT INTO t1 VALUES(3, randomblob(1503));
|
|
# INSERT INTO t1 VALUES(4, randomblob(1504));
|
|
# INSERT INTO t1
|
|
# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
|
|
# INSERT INTO t1
|
|
# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
|
|
# INSERT INTO t1
|
|
# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
|
|
# INSERT INTO t1
|
|
# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
|
|
# }
|
|
# faultsim_save_and_close
|
|
# } {}
|
|
# do_faultsim_test pagerfault-6 -prep {
|
|
# faultsim_restore_and_reopen
|
|
# } -body {
|
|
# execsql {
|
|
# BEGIN;
|
|
# UPDATE t1 SET x=x+4 WHERE x=1;
|
|
# SAVEPOINT one;
|
|
# UPDATE t1 SET x=x+4 WHERE x=2;
|
|
# SAVEPOINT three;
|
|
# UPDATE t1 SET x=x+4 WHERE x=3;
|
|
# SAVEPOINT four;
|
|
# UPDATE t1 SET x=x+4 WHERE x=4;
|
|
# RELEASE three;
|
|
# COMMIT;
|
|
# SELECT DISTINCT x FROM t1;
|
|
# }
|
|
# } -test {
|
|
# faultsim_test_result {0 {5 6 7 8}}
|
|
# faultsim_integrity_check
|
|
# }
|
|
#
|
|
|
|
# This is designed to provoke a special case in the pager code:
|
|
#
|
|
# If an error (specifically, a FULL or IOERR error) occurs while writing a
|
|
# dirty page to the file-system in order to free up memory, the pager enters
|
|
# the "error state". An IO error causes SQLite to roll back the current
|
|
# transaction (exiting the error state). A FULL error, however, may only
|
|
# rollback the current statement.
|
|
#
|
|
# This block tests that nothing goes wrong if a FULL error occurs while
|
|
# writing a dirty page out to free memory from within a statement that has
|
|
# opened a statement transaction.
|
|
#
|
|
do_test pagerfault-7-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
|
BEGIN;
|
|
INSERT INTO t2 VALUES(NULL, randomblob(1500));
|
|
INSERT INTO t2 VALUES(NULL, randomblob(1500));
|
|
INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 4
|
|
INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 8
|
|
INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 16
|
|
INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 32
|
|
INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 64
|
|
COMMIT;
|
|
CREATE TABLE t1(a PRIMARY KEY, b);
|
|
INSERT INTO t1 SELECT * FROM t2;
|
|
DROP TABLE t2;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-7 -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql {
|
|
PRAGMA cache_size = 10;
|
|
BEGIN;
|
|
UPDATE t1 SET b = randomblob(1500);
|
|
}
|
|
} -body {
|
|
execsql { UPDATE t1 SET a = 65, b = randomblob(1500) WHERE (a+1)>200 }
|
|
execsql COMMIT
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
do_test pagerfault-8-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA auto_vacuum = 1;
|
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
|
BEGIN;
|
|
INSERT INTO t1 VALUES(NULL, randomblob(1500));
|
|
INSERT INTO t1 VALUES(NULL, randomblob(1500));
|
|
INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 4
|
|
INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 8
|
|
INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 16
|
|
INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 32
|
|
INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 64
|
|
COMMIT;
|
|
}
|
|
faultsim_save_and_close
|
|
set filesize [file size test.db]
|
|
set {} {}
|
|
} {}
|
|
do_test pagerfault-8-pre2 {
|
|
faultsim_restore_and_reopen
|
|
execsql { DELETE FROM t1 WHERE a>32 }
|
|
expr {[file size test.db] < $filesize}
|
|
} {1}
|
|
do_faultsim_test pagerfault-8 -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql {
|
|
BEGIN;
|
|
DELETE FROM t1 WHERE a>32;
|
|
}
|
|
} -body {
|
|
execsql COMMIT
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# This test case is specially designed so that during a savepoint
|
|
# rollback, a new cache entry must be allocated (see comments surrounding
|
|
# the call to sqlite3PagerAcquire() from within pager_playback_one_page()
|
|
# for details). Test the effects of injecting an OOM at this point.
|
|
#
|
|
do_test pagerfault-9-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA auto_vacuum = incremental;
|
|
CREATE TABLE t1(x);
|
|
CREATE TABLE t2(y);
|
|
CREATE TABLE t3(z);
|
|
|
|
INSERT INTO t1 VALUES(randomblob(900));
|
|
INSERT INTO t1 VALUES(randomblob(900));
|
|
DELETE FROM t1;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-9.1 -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql {
|
|
BEGIN;
|
|
INSERT INTO t1 VALUES(randomblob(900));
|
|
INSERT INTO t1 VALUES(randomblob(900));
|
|
DROP TABLE t3;
|
|
DROP TABLE t2;
|
|
SAVEPOINT abc;
|
|
PRAGMA incremental_vacuum;
|
|
}
|
|
} -body {
|
|
execsql {
|
|
ROLLBACK TO abc;
|
|
COMMIT;
|
|
PRAGMA freelist_count
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 2}
|
|
faultsim_integrity_check
|
|
|
|
set sl [db one { SELECT COALESCE(sum(length(x)), 'null') FROM t1 }]
|
|
if {$sl!="null" && $sl!=1800} {
|
|
error "Content looks no good... ($sl)"
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault injection with a temporary database file.
|
|
#
|
|
foreach v {a b} {
|
|
do_faultsim_test pagerfault-10$v -prep {
|
|
sqlite3 db ""
|
|
db func a_string a_string;
|
|
execsql {
|
|
PRAGMA cache_size = 10;
|
|
BEGIN;
|
|
CREATE TABLE xx(a, b, UNIQUE(a, b));
|
|
INSERT INTO xx VALUES(a_string(200), a_string(200));
|
|
INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx;
|
|
INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx;
|
|
INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx;
|
|
INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx;
|
|
COMMIT;
|
|
}
|
|
} -body {
|
|
execsql { UPDATE xx SET a = a_string(300) }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
if {$::v == "b"} { execsql { PRAGMA journal_mode = TRUNCATE } }
|
|
faultsim_integrity_check
|
|
faultsim_integrity_check
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault injection with transaction savepoints (savepoints created
|
|
# when a SAVEPOINT command is executed outside of any other savepoint
|
|
# or transaction context).
|
|
#
|
|
do_test pagerfault-9-pre1 {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string;
|
|
execsql {
|
|
PRAGMA auto_vacuum = on;
|
|
CREATE TABLE t1(x UNIQUE);
|
|
CREATE TABLE t2(y UNIQUE);
|
|
CREATE TABLE t3(z UNIQUE);
|
|
BEGIN;
|
|
INSERT INTO t1 VALUES(a_string(202));
|
|
INSERT INTO t2 VALUES(a_string(203));
|
|
INSERT INTO t3 VALUES(a_string(204));
|
|
INSERT INTO t1 SELECT a_string(202) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(203) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(204) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(205) FROM t1;
|
|
INSERT INTO t2 SELECT a_string(length(x)) FROM t1;
|
|
INSERT INTO t3 SELECT a_string(length(x)) FROM t1;
|
|
COMMIT;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-11 -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql { PRAGMA cache_size = 10 }
|
|
} -body {
|
|
execsql {
|
|
SAVEPOINT trans;
|
|
UPDATE t2 SET y = y||'2';
|
|
INSERT INTO t3 SELECT * FROM t2;
|
|
DELETE FROM t1;
|
|
ROLLBACK TO trans;
|
|
UPDATE t1 SET x = x||'3';
|
|
INSERT INTO t2 SELECT * FROM t1;
|
|
DELETE FROM t3;
|
|
RELEASE trans;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault injection when writing to a database file that resides on
|
|
# a file-system with a sector-size larger than the database page-size.
|
|
#
|
|
do_test pagerfault-12-pre1 {
|
|
testvfs ss_layer -default 1
|
|
ss_layer sectorsize 4096
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string;
|
|
|
|
execsql {
|
|
PRAGMA page_size = 1024;
|
|
PRAGMA journal_mode = PERSIST;
|
|
PRAGMA cache_size = 10;
|
|
BEGIN;
|
|
CREATE TABLE t1(x, y UNIQUE);
|
|
INSERT INTO t1 VALUES(a_string(333), a_string(444));
|
|
INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(44), a_string(55) FROM t1 LIMIT 13;
|
|
COMMIT;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
|
|
do_faultsim_test pagerfault-12a -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql { PRAGMA cache_size = 10 }
|
|
db func a_string a_string;
|
|
} -body {
|
|
execsql {
|
|
UPDATE t1 SET x = a_string(length(x)), y = a_string(length(y));
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
do_test pagerfault-12-pre2 {
|
|
faultsim_restore_and_reopen
|
|
execsql {
|
|
CREATE TABLE t2 AS SELECT * FROM t1 LIMIT 10;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-12b -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string;
|
|
execsql { SELECT * FROM t1 }
|
|
} -body {
|
|
set sql(1) { UPDATE t2 SET x = a_string(280) }
|
|
set sql(2) { UPDATE t1 SET x = a_string(280) WHERE rowid = 5 }
|
|
|
|
db eval { SELECT rowid FROM t1 LIMIT 2 } { db eval $sql($rowid) }
|
|
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
catch { db close }
|
|
ss_layer delete
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault injection when SQLite opens a database where the size of the
|
|
# database file is zero bytes but the accompanying journal file is larger
|
|
# than that. In this scenario SQLite should delete the journal file
|
|
# without rolling it back, even if it is in all other respects a valid
|
|
# hot-journal file.
|
|
#
|
|
do_test pagerfault-13-pre1 {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string;
|
|
execsql {
|
|
PRAGMA journal_mode = PERSIST;
|
|
BEGIN;
|
|
CREATE TABLE t1(x, y UNIQUE);
|
|
INSERT INTO t1 VALUES(a_string(333), a_string(444));
|
|
COMMIT;
|
|
}
|
|
db close
|
|
forcedelete test.db
|
|
faultsim_save
|
|
} {}
|
|
do_faultsim_test pagerfault-13 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql { CREATE TABLE xx(a, b) }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Test fault injection into a small backup operation.
|
|
#
|
|
do_test pagerfault-14-pre1 {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string;
|
|
execsql {
|
|
PRAGMA journal_mode = PERSIST;
|
|
ATTACH 'test.db2' AS two;
|
|
BEGIN;
|
|
CREATE TABLE t1(x, y UNIQUE);
|
|
CREATE TABLE two.t2(x, y UNIQUE);
|
|
INSERT INTO t1 VALUES(a_string(333), a_string(444));
|
|
INSERT INTO t2 VALUES(a_string(333), a_string(444));
|
|
COMMIT;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
|
|
do_faultsim_test pagerfault-14a -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
if {[catch {db backup test.db2} msg]} { error [regsub {.*: } $msg {}] }
|
|
} -test {
|
|
faultsim_test_result {0 {}} {1 {}} {1 {SQL logic error}}
|
|
}
|
|
|
|
# If TEMP_STORE is 2 or greater, then the database [db2] will be created
|
|
# as an in-memory database. This test will not work in that case, as it
|
|
# is not possible to change the page-size of an in-memory database. Even
|
|
# using the backup API.
|
|
#
|
|
# Update: It is no longer possible to change the page size of any temp
|
|
# database after it has been created.
|
|
#
|
|
do_faultsim_test pagerfault-14b -prep {
|
|
catch { db2 close }
|
|
faultsim_restore_and_reopen
|
|
sqlite3 db2 ""
|
|
db2 eval { PRAGMA page_size = 4096; CREATE TABLE xx(a) }
|
|
} -body {
|
|
sqlite3_backup B db2 main db main
|
|
B step 200
|
|
set rc [B finish]
|
|
if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR}
|
|
if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] }
|
|
set {} {}
|
|
} -test {
|
|
faultsim_test_result {1 {attempt to write a readonly database}} \
|
|
{1 {sqlite3_backup_init() failed}}
|
|
}
|
|
|
|
do_faultsim_test pagerfault-14c -prep {
|
|
catch { db2 close }
|
|
faultsim_restore_and_reopen
|
|
sqlite3 db2 test.db2
|
|
db2 eval {
|
|
PRAGMA synchronous = off;
|
|
PRAGMA page_size = 4096;
|
|
CREATE TABLE xx(a);
|
|
}
|
|
} -body {
|
|
sqlite3_backup B db2 main db main
|
|
B step 200
|
|
set rc [B finish]
|
|
if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR}
|
|
if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] }
|
|
set {} {}
|
|
} -test {
|
|
faultsim_test_result {0 {}} {1 {sqlite3_backup_init() failed}}
|
|
}
|
|
|
|
do_test pagerfault-15-pre1 {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string;
|
|
execsql {
|
|
BEGIN;
|
|
CREATE TABLE t1(x, y UNIQUE);
|
|
INSERT INTO t1 VALUES(a_string(11), a_string(22));
|
|
INSERT INTO t1 VALUES(a_string(11), a_string(22));
|
|
COMMIT;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-15 -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string;
|
|
} -body {
|
|
db eval { SELECT * FROM t1 LIMIT 1 } {
|
|
execsql {
|
|
BEGIN; INSERT INTO t1 VALUES(a_string(333), a_string(555)); COMMIT;
|
|
BEGIN; INSERT INTO t1 VALUES(a_string(333), a_string(555)); COMMIT;
|
|
}
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
|
|
do_test pagerfault-16-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql { CREATE TABLE t1(x, y UNIQUE) }
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-16 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql {
|
|
PRAGMA locking_mode = exclusive;
|
|
PRAGMA journal_mode = wal;
|
|
INSERT INTO t1 VALUES(1, 2);
|
|
INSERT INTO t1 VALUES(3, 4);
|
|
PRAGMA journal_mode = delete;
|
|
INSERT INTO t1 VALUES(4, 5);
|
|
PRAGMA journal_mode = wal;
|
|
INSERT INTO t1 VALUES(6, 7);
|
|
PRAGMA journal_mode = persist;
|
|
INSERT INTO t1 VALUES(8, 9);
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {exclusive wal delete wal persist}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault injection while changing into and out of WAL mode.
|
|
#
|
|
do_test pagerfault-17-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
CREATE TABLE t1(a PRIMARY KEY, b);
|
|
INSERT INTO t1 VALUES(1862, 'Botha');
|
|
INSERT INTO t1 VALUES(1870, 'Smuts');
|
|
INSERT INTO t1 VALUES(1866, 'Hertzog');
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-17a -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql {
|
|
PRAGMA journal_mode = wal;
|
|
PRAGMA journal_mode = delete;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {wal delete}}
|
|
faultsim_integrity_check
|
|
}
|
|
do_faultsim_test pagerfault-17b -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql { PRAGMA synchronous = OFF }
|
|
} -body {
|
|
execsql {
|
|
PRAGMA journal_mode = wal;
|
|
INSERT INTO t1 VALUES(22, 'Clarke');
|
|
PRAGMA journal_mode = delete;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {wal delete}}
|
|
faultsim_integrity_check
|
|
}
|
|
do_faultsim_test pagerfault-17c -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql {
|
|
PRAGMA locking_mode = exclusive;
|
|
PRAGMA journal_mode = wal;
|
|
}
|
|
} -body {
|
|
execsql { PRAGMA journal_mode = delete }
|
|
} -test {
|
|
faultsim_test_result {0 delete}
|
|
faultsim_integrity_check
|
|
}
|
|
do_faultsim_test pagerfault-17d -prep {
|
|
catch { db2 close }
|
|
faultsim_restore_and_reopen
|
|
sqlite3 db2 test.db
|
|
execsql { PRAGMA journal_mode = delete }
|
|
execsql { PRAGMA journal_mode = wal }
|
|
execsql { INSERT INTO t1 VALUES(99, 'Bradman') } db2
|
|
} -body {
|
|
execsql { PRAGMA journal_mode = delete }
|
|
} -test {
|
|
faultsim_test_result {1 {database is locked}}
|
|
faultsim_integrity_check
|
|
}
|
|
do_faultsim_test pagerfault-17e -prep {
|
|
catch { db2 close }
|
|
faultsim_restore_and_reopen
|
|
sqlite3 db2 test.db
|
|
execsql { PRAGMA journal_mode = delete }
|
|
execsql { PRAGMA journal_mode = wal }
|
|
set ::chan [launch_testfixture]
|
|
testfixture $::chan {
|
|
sqlite3 db test.db
|
|
db eval { INSERT INTO t1 VALUES(101, 'Latham') }
|
|
}
|
|
catch { testfixture $::chan sqlite_abort }
|
|
catch { close $::chan }
|
|
} -body {
|
|
execsql { PRAGMA journal_mode = delete }
|
|
} -test {
|
|
faultsim_test_result {0 delete}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault-injection when changing from journal_mode=persist to
|
|
# journal_mode=delete (this involves deleting the journal file).
|
|
#
|
|
do_test pagerfault-18-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
CREATE TABLE qq(x);
|
|
INSERT INTO qq VALUES('Herbert');
|
|
INSERT INTO qq VALUES('Macalister');
|
|
INSERT INTO qq VALUES('Mackenzie');
|
|
INSERT INTO qq VALUES('Lilley');
|
|
INSERT INTO qq VALUES('Palmer');
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-18 -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql {
|
|
PRAGMA journal_mode = PERSIST;
|
|
INSERT INTO qq VALUES('Beatty');
|
|
}
|
|
} -body {
|
|
execsql { PRAGMA journal_mode = delete }
|
|
} -test {
|
|
faultsim_test_result {0 delete}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
do_faultsim_test pagerfault-19a -prep {
|
|
sqlite3 db :memory:
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA auto_vacuum = FULL;
|
|
BEGIN;
|
|
CREATE TABLE t1(a, b);
|
|
INSERT INTO t1 VALUES(a_string(5000), a_string(6000));
|
|
COMMIT;
|
|
}
|
|
} -body {
|
|
execsql {
|
|
CREATE TABLE t2(a, b);
|
|
INSERT INTO t2 SELECT * FROM t1;
|
|
DELETE FROM t1;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
|
|
do_test pagerfault-19-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA auto_vacuum = FULL;
|
|
CREATE TABLE t1(x); INSERT INTO t1 VALUES(1);
|
|
CREATE TABLE t2(x); INSERT INTO t2 VALUES(2);
|
|
CREATE TABLE t3(x); INSERT INTO t3 VALUES(3);
|
|
CREATE TABLE t4(x); INSERT INTO t4 VALUES(4);
|
|
CREATE TABLE t5(x); INSERT INTO t5 VALUES(5);
|
|
CREATE TABLE t6(x); INSERT INTO t6 VALUES(6);
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-19b -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql {
|
|
BEGIN;
|
|
UPDATE t4 SET x = x+1;
|
|
UPDATE t6 SET x = x+1;
|
|
SAVEPOINT one;
|
|
UPDATE t3 SET x = x+1;
|
|
SAVEPOINT two;
|
|
DROP TABLE t2;
|
|
ROLLBACK TO one;
|
|
COMMIT;
|
|
SELECT * FROM t3;
|
|
SELECT * FROM t4;
|
|
SELECT * FROM t6;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {3 5 7}}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# This tests fault-injection in a special case in the auto-vacuum code.
|
|
#
|
|
do_test pagerfault-20-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA cache_size = 10;
|
|
PRAGMA auto_vacuum = FULL;
|
|
CREATE TABLE t0(a, b);
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-20 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql {
|
|
BEGIN;
|
|
CREATE TABLE t1(a, b);
|
|
CREATE TABLE t2(a, b);
|
|
DROP TABLE t1;
|
|
COMMIT;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
|
|
do_test pagerfault-21-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA cache_size = 10;
|
|
CREATE TABLE t0(a PRIMARY KEY, b);
|
|
INSERT INTO t0 VALUES(1, 2);
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-21 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
db eval { SELECT * FROM t0 LIMIT 1 } {
|
|
db eval { INSERT INTO t0 SELECT a+1, b FROM t0 }
|
|
db eval { INSERT INTO t0 SELECT a+2, b FROM t0 }
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Test fault-injection and rollback when the nReserve header value
|
|
# is non-zero.
|
|
#
|
|
do_test pagerfault-21-pre1 {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA page_size = 1024;
|
|
PRAGMA journal_mode = WAL;
|
|
PRAGMA journal_mode = DELETE;
|
|
}
|
|
db close
|
|
hexio_write test.db 20 10
|
|
hexio_write test.db 105 03F0
|
|
sqlite3 db test.db
|
|
db func a_string a_string
|
|
execsql {
|
|
CREATE TABLE t0(a PRIMARY KEY, b UNIQUE);
|
|
INSERT INTO t0 VALUES(a_string(222), a_string(333));
|
|
INSERT INTO t0 VALUES(a_string(223), a_string(334));
|
|
INSERT INTO t0 VALUES(a_string(224), a_string(335));
|
|
INSERT INTO t0 VALUES(a_string(225), a_string(336));
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
|
|
do_faultsim_test pagerfault-21 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql { INSERT INTO t0 SELECT a||'x', b||'x' FROM t0 }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
ifcapable crashtest {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA page_size = 1024;
|
|
PRAGMA journal_mode = WAL;
|
|
PRAGMA journal_mode = DELETE;
|
|
}
|
|
db close
|
|
hexio_write test.db 20 10
|
|
hexio_write test.db 105 03F0
|
|
|
|
sqlite3 db test.db
|
|
db func a_string a_string
|
|
execsql {
|
|
CREATE TABLE t0(a PRIMARY KEY, b UNIQUE);
|
|
INSERT INTO t0 VALUES(a_string(222), a_string(333));
|
|
INSERT INTO t0 VALUES(a_string(223), a_string(334));
|
|
}
|
|
faultsim_save_and_close
|
|
|
|
for {set iTest 1} {$iTest<50} {incr iTest} {
|
|
do_test pagerfault-21.crash.$iTest.1 {
|
|
crashsql -delay 1 -file test.db -seed $iTest {
|
|
BEGIN;
|
|
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
|
|
INSERT INTO t1 SELECT a, b FROM t0;
|
|
COMMIT;
|
|
}
|
|
} {1 {child process exited abnormally}}
|
|
do_test pagerfault-22.$iTest.2 {
|
|
sqlite3 db test.db
|
|
execsql { PRAGMA integrity_check }
|
|
} {ok}
|
|
db close
|
|
}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
# When a 3.7.0 client opens a write-transaction on a database file that
|
|
# has been appended to or truncated by a pre-370 client, it updates
|
|
# the db-size in the file header immediately. This test case provokes
|
|
# errors during that operation.
|
|
#
|
|
do_test pagerfault-22-pre1 {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA page_size = 1024;
|
|
PRAGMA auto_vacuum = 0;
|
|
CREATE TABLE t1(a);
|
|
CREATE INDEX i1 ON t1(a);
|
|
INSERT INTO t1 VALUES(a_string(3000));
|
|
CREATE TABLE t2(a);
|
|
INSERT INTO t2 VALUES(1);
|
|
}
|
|
db close
|
|
sql36231 { INSERT INTO t1 VALUES(a_string(3000)) }
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-22 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql { INSERT INTO t2 VALUES(2) }
|
|
execsql { SELECT * FROM t2 }
|
|
} -test {
|
|
faultsim_test_result {0 {1 2}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Provoke an OOM error during a commit of multi-file transaction. One of
|
|
# the databases written during the transaction is an in-memory database.
|
|
# This test causes rollback of the in-memory database after CommitPhaseOne()
|
|
# has successfully returned. i.e. the series of calls for the aborted commit
|
|
# is:
|
|
#
|
|
# PagerCommitPhaseOne(<in-memory-db>) -> SQLITE_OK
|
|
# PagerCommitPhaseOne(<file-db>) -> SQLITE_IOERR
|
|
# PagerRollback(<in-memory-db>)
|
|
# PagerRollback(<file-db>)
|
|
#
|
|
do_faultsim_test pagerfault-23 -prep {
|
|
sqlite3 db :memory:
|
|
foreach f [glob -nocomplain test.db*] { forcedelete $f }
|
|
db eval {
|
|
ATTACH 'test.db2' AS aux;
|
|
CREATE TABLE t1(a, b);
|
|
CREATE TABLE aux.t2(a, b);
|
|
}
|
|
} -body {
|
|
execsql {
|
|
BEGIN;
|
|
INSERT INTO t1 VALUES(1,2);
|
|
INSERT INTO t2 VALUES(3,4);
|
|
COMMIT;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
do_faultsim_test pagerfault-24 -prep {
|
|
faultsim_delete_and_reopen
|
|
db eval { PRAGMA temp_store = file }
|
|
execsql { CREATE TABLE x(a, b) }
|
|
} -body {
|
|
execsql { CREATE TEMP TABLE t1(a, b) }
|
|
} -test {
|
|
faultsim_test_result {0 {}} \
|
|
{1 {unable to open a temporary database file for storing temporary tables}}
|
|
set ic [db eval { PRAGMA temp.integrity_check }]
|
|
if {$ic != "ok"} { error "Integrity check: $ic" }
|
|
}
|
|
|
|
proc lockrows {n} {
|
|
if {$n==0} { return "" }
|
|
db eval { SELECT * FROM t1 WHERE oid = $n } {
|
|
return [lockrows [expr {$n-1}]]
|
|
}
|
|
}
|
|
|
|
|
|
do_test pagerfault-25-pre1 {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA page_size = 1024;
|
|
PRAGMA auto_vacuum = 0;
|
|
CREATE TABLE t1(a);
|
|
INSERT INTO t1 VALUES(a_string(500));
|
|
INSERT INTO t1 SELECT a_string(500) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(500) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(500) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(500) FROM t1;
|
|
INSERT INTO t1 SELECT a_string(500) FROM t1;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-25 -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string
|
|
set ::channel [db incrblob -readonly t1 a 1]
|
|
execsql {
|
|
PRAGMA cache_size = 10;
|
|
BEGIN;
|
|
INSERT INTO t1 VALUES(a_string(3000));
|
|
INSERT INTO t1 VALUES(a_string(3000));
|
|
}
|
|
} -body {
|
|
lockrows 30
|
|
} -test {
|
|
catch { lockrows 30 }
|
|
catch { db eval COMMIT }
|
|
close $::channel
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
|
|
do_faultsim_test pagerfault-26 -prep {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
PRAGMA page_size = 1024;
|
|
PRAGMA journal_mode = truncate;
|
|
PRAGMA auto_vacuum = full;
|
|
PRAGMA locking_mode=exclusive;
|
|
CREATE TABLE t1(a, b);
|
|
INSERT INTO t1 VALUES(1, 2);
|
|
PRAGMA page_size = 4096;
|
|
}
|
|
} -body {
|
|
execsql {
|
|
VACUUM;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
|
|
set contents [db eval {SELECT * FROM t1}]
|
|
if {$contents != "1 2"} { error "Bad database contents ($contents)" }
|
|
|
|
if {[atomic_batch_write test.db]==0} {
|
|
set sz [file size test.db]
|
|
if {$testrc!=0 && $sz!=1024*3 && $sz!=4096*3} {
|
|
error "Expected file size 3072 or 12288 bytes - actual size $sz bytes"
|
|
}
|
|
if {$testrc==0 && $sz!=4096*3} {
|
|
error "Expected file size to be 12288 bytes - actual size $sz bytes"
|
|
}
|
|
}
|
|
}
|
|
|
|
do_test pagerfault-27-pre {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA page_size = 1024;
|
|
CREATE TABLE t1(a, b);
|
|
CREATE TABLE t2(a UNIQUE, b UNIQUE);
|
|
INSERT INTO t2 VALUES( a_string(800), a_string(800) );
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t1 VALUES (a_string(20000), a_string(20000));
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-27 -faults ioerr-persistent -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA cache_size = 10;
|
|
BEGIN EXCLUSIVE;
|
|
}
|
|
set ::channel [db incrblob t1 a 1]
|
|
} -body {
|
|
puts $::channel [string repeat abc 6000]
|
|
flush $::channel
|
|
} -test {
|
|
catchsql { UPDATE t2 SET a = a_string(800), b = a_string(800) }
|
|
catch { close $::channel }
|
|
catchsql { ROLLBACK }
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
do_test pagerfault-28-pre {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA page_size = 512;
|
|
|
|
PRAGMA journal_mode = wal;
|
|
PRAGMA wal_autocheckpoint = 0;
|
|
PRAGMA cache_size = 100000;
|
|
|
|
BEGIN;
|
|
CREATE TABLE t2(a UNIQUE, b UNIQUE);
|
|
INSERT INTO t2 VALUES( a_string(800), a_string(800) );
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
COMMIT;
|
|
CREATE TABLE t1(a PRIMARY KEY, b);
|
|
}
|
|
expr {[file size test.db-shm] >= 96*1024}
|
|
} {1}
|
|
faultsim_save_and_close
|
|
|
|
do_faultsim_test pagerfault-28a -faults oom* -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql { PRAGMA mmap_size=0 }
|
|
|
|
sqlite3 db2 test.db
|
|
db2 eval { SELECT count(*) FROM t2 }
|
|
|
|
db func a_string a_string
|
|
execsql {
|
|
BEGIN;
|
|
INSERT INTO t1 VALUES(a_string(2000), a_string(2000));
|
|
INSERT INTO t1 VALUES(a_string(2000), a_string(2000));
|
|
}
|
|
set ::STMT [sqlite3_prepare db "SELECT * FROM t1 ORDER BY a" -1 DUMMY]
|
|
sqlite3_step $::STMT
|
|
} -body {
|
|
execsql { ROLLBACK }
|
|
} -test {
|
|
db2 close
|
|
sqlite3_finalize $::STMT
|
|
catchsql { ROLLBACK }
|
|
faultsim_integrity_check
|
|
}
|
|
|
|
faultsim_restore_and_reopen
|
|
sqlite3 db2 test.db
|
|
db2 eval {SELECT count(*) FROM t2}
|
|
db close
|
|
|
|
do_faultsim_test pagerfault-28b -faults oom* -prep {
|
|
sqlite3 db test.db
|
|
} -body {
|
|
execsql { SELECT count(*) FROM t2 }
|
|
} -test {
|
|
faultsim_test_result {0 2048}
|
|
db close
|
|
}
|
|
|
|
db2 close
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Try this:
|
|
#
|
|
# 1) Put the pager in ERROR state (error during rollback)
|
|
#
|
|
# 2) Next time the connection is used inject errors into all xWrite() and
|
|
# xUnlock() calls. This causes the hot-journal rollback to fail and
|
|
# the pager to declare its locking state UNKNOWN.
|
|
#
|
|
# 3) Same again.
|
|
#
|
|
# 4a) Stop injecting errors. Allow the rollback to succeed. Check that
|
|
# the database is Ok. Or,
|
|
#
|
|
# 4b) Close and reopen the db. Check that the db is Ok.
|
|
#
|
|
proc custom_injectinstall {} {
|
|
testvfs custom -default true
|
|
custom filter {xWrite xUnlock}
|
|
}
|
|
proc custom_injectuninstall {} {
|
|
catch {db close}
|
|
catch {db2 close}
|
|
custom delete
|
|
}
|
|
proc custom_injectstart {iFail} {
|
|
custom ioerr $iFail 1
|
|
}
|
|
proc custom_injectstop {} {
|
|
custom ioerr
|
|
}
|
|
set ::FAULTSIM(custom) [list \
|
|
-injectinstall custom_injectinstall \
|
|
-injectstart custom_injectstart \
|
|
-injectstop custom_injectstop \
|
|
-injecterrlist {{1 {disk I/O error}}} \
|
|
-injectuninstall custom_injectuninstall \
|
|
]
|
|
|
|
do_test pagerfault-29-pre {
|
|
faultsim_delete_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA page_size = 1024;
|
|
PRAGMA cache_size = 5;
|
|
|
|
BEGIN;
|
|
CREATE TABLE t2(a UNIQUE, b UNIQUE);
|
|
INSERT INTO t2 VALUES( a_string(800), a_string(800) );
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2;
|
|
COMMIT;
|
|
}
|
|
expr {[file size test.db] >= 50*1024}
|
|
} {1}
|
|
faultsim_save_and_close
|
|
foreach {tn tt} {
|
|
29 { catchsql ROLLBACK }
|
|
30 { db close ; sqlite3 db test.db }
|
|
} {
|
|
do_faultsim_test pagerfault-$tn -faults custom -prep {
|
|
faultsim_restore_and_reopen
|
|
db func a_string a_string
|
|
execsql {
|
|
PRAGMA cache_size = 5;
|
|
BEGIN;
|
|
UPDATE t2 SET a = a_string(799);
|
|
}
|
|
} -body {
|
|
catchsql ROLLBACK
|
|
catchsql ROLLBACK
|
|
catchsql ROLLBACK
|
|
} -test {
|
|
eval $::tt
|
|
if {"ok" != [db one {PRAGMA integrity_check}]} {
|
|
error "integrity check failed"
|
|
}
|
|
}
|
|
}
|
|
|
|
do_test pagerfault-31-pre {
|
|
sqlite3_shutdown
|
|
sqlite3_config_uri 1
|
|
} {SQLITE_OK}
|
|
do_faultsim_test pagerfault-31 -faults oom* -body {
|
|
sqlite3 db {file:one?mode=memory&cache=shared}
|
|
db eval {
|
|
CREATE TABLE t1(x);
|
|
INSERT INTO t1 VALUES(1);
|
|
SELECT * FROM t1;
|
|
}
|
|
} -test {
|
|
faultsim_test_result {0 1} {1 {}}
|
|
catch { db close }
|
|
}
|
|
sqlite3_shutdown
|
|
sqlite3_config_uri 0
|
|
|
|
do_test pagerfault-32-pre {
|
|
reset_db
|
|
execsql {
|
|
CREATE TABLE t1(x);
|
|
INSERT INTO t1 VALUES('one');
|
|
}
|
|
} {}
|
|
faultsim_save_and_close
|
|
|
|
do_faultsim_test pagerfault-32 -prep {
|
|
faultsim_restore_and_reopen
|
|
db eval { SELECT * FROM t1; }
|
|
} -body {
|
|
execsql { SELECT * FROM t1; }
|
|
} -test {
|
|
faultsim_test_result {0 one}
|
|
}
|
|
sqlite3_shutdown
|
|
sqlite3_config_uri 0
|
|
|
|
do_faultsim_test pagerfault-33a -prep {
|
|
sqlite3 db :memory:
|
|
execsql {
|
|
CREATE TABLE t1(a, b);
|
|
INSERT INTO t1 VALUES(1, 2);
|
|
}
|
|
} -body {
|
|
execsql { VACUUM }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
do_faultsim_test pagerfault-33b -prep {
|
|
sqlite3 db ""
|
|
execsql {
|
|
CREATE TABLE t1(a, b);
|
|
INSERT INTO t1 VALUES(1, 2);
|
|
}
|
|
} -body {
|
|
execsql { VACUUM }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
|
|
do_test pagerfault-34-pre {
|
|
reset_db
|
|
execsql {
|
|
CREATE TABLE t1(x PRIMARY KEY);
|
|
}
|
|
} {}
|
|
faultsim_save_and_close
|
|
do_faultsim_test pagerfault-34 -prep {
|
|
faultsim_restore_and_reopen
|
|
execsql {
|
|
BEGIN;
|
|
INSERT INTO t1 VALUES( randomblob(4000) );
|
|
DELETE FROM t1;
|
|
}
|
|
} -body {
|
|
execsql COMMIT
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
|
|
do_test pagerfault-35-pre {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
CREATE TABLE t1(x PRIMARY KEY, y);
|
|
INSERT INTO t1 VALUES(randomblob(200), randomblob(200));
|
|
INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1;
|
|
INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1;
|
|
INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
testvfs tv -default 1
|
|
tv sectorsize 8192;
|
|
tv devchar [list]
|
|
do_faultsim_test pagerfault-35 -prep {
|
|
faultsim_restore_and_reopen
|
|
} -body {
|
|
execsql { UPDATE t1 SET x=randomblob(200) }
|
|
} -test {
|
|
faultsim_test_result {0 {}}
|
|
}
|
|
catch {db close}
|
|
tv delete
|
|
|
|
sqlite3_shutdown
|
|
sqlite3_config_uri 1
|
|
do_test pagerfault-36-pre {
|
|
faultsim_delete_and_reopen
|
|
execsql {
|
|
CREATE TABLE t1(x PRIMARY KEY, y);
|
|
INSERT INTO t1 VALUES(randomblob(200), randomblob(200));
|
|
INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1;
|
|
INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1;
|
|
INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1;
|
|
}
|
|
faultsim_save_and_close
|
|
} {}
|
|
do_faultsim_test pagerfault-36 -prep {
|
|
faultsim_restore
|
|
sqlite3 db file:test.db?cache=shared
|
|
sqlite3 db2 file:test.db?cache=shared
|
|
db2 eval {
|
|
BEGIN;
|
|
SELECT count(*) FROM sqlite_master;
|
|
}
|
|
db eval {
|
|
PRAGMA cache_size = 1;
|
|
BEGIN;
|
|
UPDATE t1 SET x = randomblob(200);
|
|
}
|
|
} -body {
|
|
execsql ROLLBACK db
|
|
} -test {
|
|
catch { db eval {UPDATE t1 SET x = randomblob(200)} }
|
|
faultsim_test_result {0 {}}
|
|
catch { db close }
|
|
catch { db2 close }
|
|
}
|
|
|
|
sqlite3_shutdown
|
|
sqlite3_config_uri 0
|
|
sqlite3_initialize
|
|
|
|
finish_test
|