diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2021-05-06 20:08:52 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2021-05-06 20:08:52 +0000 |
| commit | 8b10604cd15958e62b9d4eb62bcb925272583db1 (patch) | |
| tree | f18f8ed9fdfeeca2b9c856949a4cae7057ed84a6 /test/wal2.test | |
| parent | eccd5a4d3926c0716dd11bdf3242da56116f68c6 (diff) | |
Diffstat (limited to 'test/wal2.test')
| -rw-r--r-- | test/wal2.test | 1353 |
1 files changed, 0 insertions, 1353 deletions
diff --git a/test/wal2.test b/test/wal2.test deleted file mode 100644 index ae6134d8b57e..000000000000 --- a/test/wal2.test +++ /dev/null @@ -1,1353 +0,0 @@ -# 2010 May 5 -# -# 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 testing the operation of the library in -# "PRAGMA journal_mode=WAL" mode. -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl -source $testdir/lock_common.tcl -source $testdir/malloc_common.tcl -source $testdir/wal_common.tcl - -set testprefix wal2 - -ifcapable !wal {finish_test ; return } - -set sqlite_sync_count 0 -proc cond_incr_sync_count {adj} { - global sqlite_sync_count - if {$::tcl_platform(platform) == "windows"} { - incr sqlite_sync_count $adj - } { - ifcapable !dirsync { - incr sqlite_sync_count $adj - } - } -} - -proc set_tvfs_hdr {file args} { - - # Set $nHdr to the number of bytes in the wal-index header: - set nHdr 48 - set nInt [expr {$nHdr/4}] - - if {[llength $args]>2} { - error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"} - } - - set blob [tvfs shm $file] - if {$::tcl_platform(byteOrder)=="bigEndian"} {set fmt I} {set fmt i} - - if {[llength $args]} { - set ia [lindex $args 0] - set ib $ia - if {[llength $args]==2} { - set ib [lindex $args 1] - } - binary scan $blob a[expr $nHdr*2]a* dummy tail - set blob [binary format ${fmt}${nInt}${fmt}${nInt}a* $ia $ib $tail] - tvfs shm $file $blob - } - - binary scan $blob ${fmt}${nInt} ints - return $ints -} - -proc incr_tvfs_hdr {file idx incrval} { - set ints [set_tvfs_hdr $file] - set v [lindex $ints $idx] - incr v $incrval - lset ints $idx $v - set_tvfs_hdr $file $ints -} - - -#------------------------------------------------------------------------- -# Test case wal2-1.*: -# -# Set up a small database containing a single table. The database is not -# checkpointed during the test - all content resides in the log file. -# -# Two connections are established to the database file - a writer ([db]) -# and a reader ([db2]). For each of the 8 integer fields in the wal-index -# header (6 fields and 2 checksum values), do the following: -# -# 1. Modify the database using the writer. -# -# 2. Attempt to read the database using the reader. Before the reader -# has a chance to snapshot the wal-index header, increment one -# of the integer fields (so that the reader ends up with a corrupted -# header). -# -# 3. Check that the reader recovers the wal-index and reads the correct -# database content. -# -do_test wal2-1.0 { - proc tvfs_cb {method filename args} { - set ::filename $filename - return SQLITE_OK - } - - testvfs tvfs - tvfs script tvfs_cb - tvfs filter xShmOpen - - sqlite3 db test.db -vfs tvfs - sqlite3 db2 test.db -vfs tvfs - - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE t1(a); - } db2 - execsql { - INSERT INTO t1 VALUES(1); - INSERT INTO t1 VALUES(2); - INSERT INTO t1 VALUES(3); - INSERT INTO t1 VALUES(4); - SELECT count(a), sum(a) FROM t1; - } -} {4 10} -do_test wal2-1.1 { - execsql { SELECT count(a), sum(a) FROM t1 } db2 -} {4 10} - -set RECOVER [list \ - {0 1 lock exclusive} {1 2 lock exclusive} \ - {4 1 lock exclusive} {4 1 unlock exclusive} \ - {5 1 lock exclusive} {5 1 unlock exclusive} \ - {6 1 lock exclusive} {6 1 unlock exclusive} \ - {7 1 lock exclusive} {7 1 unlock exclusive} \ - {1 2 unlock exclusive} {0 1 unlock exclusive} \ -] -set READ [list \ - {4 1 lock shared} {4 1 unlock shared} \ -] -set INITSLOT [list \ - {4 1 lock exclusive} {4 1 unlock exclusive} \ -] - -foreach {tn iInsert res wal_index_hdr_mod wal_locks} " - 2 5 {5 15} 0 {$RECOVER $READ} - 3 6 {6 21} 1 {$RECOVER $READ} - 4 7 {7 28} 2 {$RECOVER $READ} - 5 8 {8 36} 3 {$RECOVER $READ} - 6 9 {9 45} 4 {$RECOVER $READ} - 7 10 {10 55} 5 {$RECOVER $READ} - 8 11 {11 66} 6 {$RECOVER $READ} - 9 12 {12 78} 7 {$RECOVER $READ} - 10 13 {13 91} 8 {$RECOVER $READ} - 11 14 {14 105} 9 {$RECOVER $READ} - 12 15 {15 120} -1 {$INITSLOT $READ} -" { - - do_test wal2-1.$tn.1 { - execsql { INSERT INTO t1 VALUES($iInsert) } - set ::locks [list] - proc tvfs_cb {method args} { - lappend ::locks [lindex $args 2] - return SQLITE_OK - } - tvfs filter xShmLock - if {$::wal_index_hdr_mod >= 0} { - incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1 - } - execsql { SELECT count(a), sum(a) FROM t1 } db2 - } $res - - do_test wal2-1.$tn.2 { - set ::locks - } $wal_locks -} -db close -db2 close -tvfs delete -forcedelete test.db test.db-wal test.db-journal - -#------------------------------------------------------------------------- -# This test case is very similar to the previous one, except, after -# the reader reads the corrupt wal-index header, but before it has -# a chance to re-read it under the cover of the RECOVER lock, the -# wal-index header is replaced with a valid, but out-of-date, header. -# -# Because the header checksum looks Ok, the reader does not run recovery, -# it simply drops back to a READ lock and proceeds. But because the -# header is out-of-date, the reader reads the out-of-date snapshot. -# -# After this, the header is corrupted again and the reader is allowed -# to run recovery. This time, it sees an up-to-date snapshot of the -# database file. -# -set WRITER [list 0 1 lock exclusive] -set LOCKS [list \ - {0 1 lock exclusive} {0 1 unlock exclusive} \ - {4 1 lock exclusive} {4 1 unlock exclusive} \ - {4 1 lock shared} {4 1 unlock shared} \ -] -do_test wal2-2.0 { - - testvfs tvfs - tvfs script tvfs_cb - tvfs filter xShmOpen - proc tvfs_cb {method args} { - set ::filename [lindex $args 0] - return SQLITE_OK - } - - sqlite3 db test.db -vfs tvfs - sqlite3 db2 test.db -vfs tvfs - - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE t1(a); - } db2 - execsql { - INSERT INTO t1 VALUES(1); - INSERT INTO t1 VALUES(2); - INSERT INTO t1 VALUES(3); - INSERT INTO t1 VALUES(4); - SELECT count(a), sum(a) FROM t1; - } -} {4 10} -do_test wal2-2.1 { - execsql { SELECT count(a), sum(a) FROM t1 } db2 -} {4 10} - -foreach {tn iInsert res0 res1 wal_index_hdr_mod} { - 2 5 {4 10} {5 15} 0 - 3 6 {5 15} {6 21} 1 - 4 7 {6 21} {7 28} 2 - 5 8 {7 28} {8 36} 3 - 6 9 {8 36} {9 45} 4 - 7 10 {9 45} {10 55} 5 - 8 11 {10 55} {11 66} 6 - 9 12 {11 66} {12 78} 7 -} { - tvfs filter xShmLock - - do_test wal2-2.$tn.1 { - set oldhdr [set_tvfs_hdr $::filename] - execsql { INSERT INTO t1 VALUES($iInsert) } - execsql { SELECT count(a), sum(a) FROM t1 } - } $res1 - - do_test wal2-2.$tn.2 { - set ::locks [list] - proc tvfs_cb {method args} { - set lock [lindex $args 2] - lappend ::locks $lock - if {$lock == $::WRITER} { - set_tvfs_hdr $::filename $::oldhdr - } - return SQLITE_OK - } - - if {$::wal_index_hdr_mod >= 0} { - incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1 - } - execsql { SELECT count(a), sum(a) FROM t1 } db2 - } $res0 - - do_test wal2-2.$tn.3 { - set ::locks - } $LOCKS - - do_test wal2-2.$tn.4 { - set ::locks [list] - proc tvfs_cb {method args} { - set lock [lindex $args 2] - lappend ::locks $lock - return SQLITE_OK - } - - if {$::wal_index_hdr_mod >= 0} { - incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1 - } - execsql { SELECT count(a), sum(a) FROM t1 } db2 - } $res1 -} -db close -db2 close -tvfs delete -forcedelete test.db test.db-wal test.db-journal - - -if 0 { -#------------------------------------------------------------------------- -# This test case - wal2-3.* - tests the response of the library to an -# SQLITE_BUSY when attempting to obtain a READ or RECOVER lock. -# -# wal2-3.0 - 2: SQLITE_BUSY when obtaining a READ lock -# wal2-3.3 - 6: SQLITE_BUSY when obtaining a RECOVER lock -# -do_test wal2-3.0 { - proc tvfs_cb {method args} { - if {$method == "xShmLock"} { - if {[info exists ::locked]} { return SQLITE_BUSY } - } - return SQLITE_OK - } - - proc busyhandler x { - if {$x>3} { unset -nocomplain ::locked } - return 0 - } - - testvfs tvfs - tvfs script tvfs_cb - sqlite3 db test.db -vfs tvfs - db busy busyhandler - - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE t1(a); - INSERT INTO t1 VALUES(1); - INSERT INTO t1 VALUES(2); - INSERT INTO t1 VALUES(3); - INSERT INTO t1 VALUES(4); - } - - set ::locked 1 - info exists ::locked -} {1} -do_test wal2-3.1 { - execsql { SELECT count(a), sum(a) FROM t1 } -} {4 10} -do_test wal2-3.2 { - info exists ::locked -} {0} - -do_test wal2-3.3 { - proc tvfs_cb {method args} { - if {$method == "xShmLock"} { - if {[info exists ::sabotage]} { - unset -nocomplain ::sabotage - incr_tvfs_hdr [lindex $args 0] 1 1 - } - if {[info exists ::locked] && [lindex $args 2] == "RECOVER"} { - return SQLITE_BUSY - } - } - return SQLITE_OK - } - set ::sabotage 1 - set ::locked 1 - list [info exists ::sabotage] [info exists ::locked] -} {1 1} -do_test wal2-3.4 { - execsql { SELECT count(a), sum(a) FROM t1 } -} {4 10} -do_test wal2-3.5 { - list [info exists ::sabotage] [info exists ::locked] -} {0 0} -db close -tvfs delete -forcedelete test.db test.db-wal test.db-journal - -} - -#------------------------------------------------------------------------- -# Test that a database connection using a VFS that does not support the -# xShmXXX interfaces cannot open a WAL database. -# -do_test wal2-4.1 { - sqlite3 db test.db - execsql { - PRAGMA auto_vacuum = 0; - PRAGMA journal_mode = WAL; - CREATE TABLE data(x); - INSERT INTO data VALUES('need xShmOpen to see this'); - PRAGMA wal_checkpoint; - } - # Three pages in the WAL file at this point: One copy of page 1 and two - # of the root page for table "data". -} {wal 0 3 3} -do_test wal2-4.2 { - db close - testvfs tvfs -noshm 1 - sqlite3 db test.db -vfs tvfs - catchsql { SELECT * FROM data } -} {1 {unable to open database file}} -do_test wal2-4.3 { - db close - testvfs tvfs - sqlite3 db test.db -vfs tvfs - catchsql { SELECT * FROM data } -} {0 {{need xShmOpen to see this}}} -db close -tvfs delete - -#------------------------------------------------------------------------- -# Test that if a database connection is forced to run recovery before it -# can perform a checkpoint, it does not transition into RECOVER state. -# -# UPDATE: This has now changed. When running a checkpoint, if recovery is -# required the client grabs all exclusive locks (just as it would for a -# recovery performed as a pre-cursor to a normal database transaction). -# -set expected_locks [list] -lappend expected_locks {1 1 lock exclusive} ;# Lock checkpoint -lappend expected_locks {0 1 lock exclusive} ;# Lock writer -lappend expected_locks {2 1 lock exclusive} ;# Lock recovery -# lappend expected_locks {4 4 lock exclusive} ;# Lock all aReadMark[] -lappend expected_locks {4 1 lock exclusive} ;# Lock aReadMark[1] -lappend expected_locks {4 1 unlock exclusive} ;# Unlock aReadMark[1] -lappend expected_locks {5 1 lock exclusive} -lappend expected_locks {5 1 unlock exclusive} -lappend expected_locks {6 1 lock exclusive} -lappend expected_locks {6 1 unlock exclusive} -lappend expected_locks {7 1 lock exclusive} -lappend expected_locks {7 1 unlock exclusive} -lappend expected_locks {2 1 unlock exclusive} ;# Unlock recovery -# lappend expected_locks {4 4 unlock exclusive} ;# Unlock all aReadMark[] -lappend expected_locks {0 1 unlock exclusive} ;# Unlock writer -lappend expected_locks {3 1 lock exclusive} ;# Lock aReadMark[0] -lappend expected_locks {3 1 unlock exclusive} ;# Unlock aReadMark[0] -lappend expected_locks {1 1 unlock exclusive} ;# Unlock checkpoint -do_test wal2-5.1 { - proc tvfs_cb {method args} { - set ::shm_file [lindex $args 0] - if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] } - return $::tvfs_cb_return - } - set tvfs_cb_return SQLITE_OK - - testvfs tvfs - tvfs script tvfs_cb - - sqlite3 db test.db -vfs tvfs - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE x(y); - INSERT INTO x VALUES(1); - } - - incr_tvfs_hdr $::shm_file 1 1 - set ::locks [list] - execsql { PRAGMA wal_checkpoint } - set ::locks -} $expected_locks -db close -tvfs delete - -#------------------------------------------------------------------------- -# This block, test cases wal2-6.*, tests the operation of WAL with -# "PRAGMA locking_mode=EXCLUSIVE" set. -# -# wal2-6.1.*: Changing to WAL mode before setting locking_mode=exclusive. -# -# wal2-6.2.*: Changing to WAL mode after setting locking_mode=exclusive. -# -# wal2-6.3.*: Changing back to rollback mode from WAL mode after setting -# locking_mode=exclusive. -# -# wal2-6.4.*: Check that xShmLock calls are omitted in exclusive locking -# mode. -# -# wal2-6.5.*: -# -# wal2-6.6.*: Check that if the xShmLock() to reaquire a WAL read-lock when -# exiting exclusive mode fails (i.e. SQLITE_IOERR), then the -# connection silently remains in exclusive mode. -# -do_test wal2-6.1.1 { - forcedelete test.db test.db-wal test.db-journal - sqlite3 db test.db - execsql { - Pragma Journal_Mode = Wal; - } -} {wal} -do_test wal2-6.1.2 { - execsql { PRAGMA lock_status } -} {main unlocked temp closed} -do_test wal2-6.1.3 { - execsql { - SELECT * FROM sqlite_master; - Pragma Locking_Mode = Exclusive; - } - execsql { - BEGIN; - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, 2); - COMMIT; - PRAGMA lock_status; - } -} {main exclusive temp closed} -do_test wal2-6.1.4 { - execsql { - PRAGMA locking_mode = normal; - PRAGMA lock_status; - } -} {normal main exclusive temp closed} -do_test wal2-6.1.5 { - execsql { - SELECT * FROM t1; - PRAGMA lock_status; - } -} {1 2 main shared temp closed} -do_test wal2-6.1.6 { - execsql { - INSERT INTO t1 VALUES(3, 4); - PRAGMA lock_status; - } -} {main shared temp closed} -db close - -do_test wal2-6.2.1 { - forcedelete test.db test.db-wal test.db-journal - sqlite3 db test.db - execsql { - Pragma Locking_Mode = Exclusive; - Pragma Journal_Mode = Wal; - Pragma Lock_Status; - } -} {exclusive wal main exclusive temp closed} -do_test wal2-6.2.2 { - execsql { - BEGIN; - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, 2); - COMMIT; - Pragma loCK_STATus; - } -} {main exclusive temp closed} -do_test wal2-6.2.3 { - db close - sqlite3 db test.db - execsql { SELECT * FROM sqlite_master } - execsql { PRAGMA LOCKING_MODE = EXCLUSIVE } -} {exclusive} -do_test wal2-6.2.4 { - execsql { - SELECT * FROM t1; - pragma lock_status; - } -} {1 2 main shared temp closed} -do_test wal2-6.2.5 { - execsql { - INSERT INTO t1 VALUES(3, 4); - pragma lock_status; - } -} {main exclusive temp closed} -do_test wal2-6.2.6 { - execsql { - PRAGMA locking_mode = NORMAL; - pragma lock_status; - } -} {normal main exclusive temp closed} -do_test wal2-6.2.7 { - execsql { - BEGIN IMMEDIATE; COMMIT; - pragma lock_status; - } -} {main shared temp closed} -do_test wal2-6.2.8 { - execsql { - PRAGMA locking_mode = EXCLUSIVE; - BEGIN IMMEDIATE; COMMIT; - PRAGMA locking_mode = NORMAL; - } - execsql { - SELECT * FROM t1; - pragma lock_status; - } -} {1 2 3 4 main shared temp closed} -do_test wal2-6.2.9 { - execsql { - INSERT INTO t1 VALUES(5, 6); - SELECT * FROM t1; - pragma lock_status; - } -} {1 2 3 4 5 6 main shared temp closed} -db close - -do_test wal2-6.3.1 { - forcedelete test.db test.db-wal test.db-journal - sqlite3 db test.db - execsql { - PRAGMA journal_mode = WAL; - PRAGMA locking_mode = exclusive; - BEGIN; - CREATE TABLE t1(x); - INSERT INTO t1 VALUES('Chico'); - INSERT INTO t1 VALUES('Harpo'); - COMMIT; - } - list [file exists test.db-wal] [file exists test.db-journal] -} {1 0} -do_test wal2-6.3.2 { - execsql { PRAGMA journal_mode = DELETE } - file exists test.db-wal -} {0} -do_test wal2-6.3.3 { - execsql { PRAGMA lock_status } -} {main exclusive temp closed} -do_test wal2-6.3.4 { - execsql { - BEGIN; - INSERT INTO t1 VALUES('Groucho'); - } -} {} -if {[atomic_batch_write test.db]==0} { - do_test wal2-6.3.4.1 { - list [file exists test.db-wal] [file exists test.db-journal] - } {0 1} -} -do_test wal2-6.3.5 { - execsql { PRAGMA lock_status } -} {main exclusive temp closed} -do_test wal2-6.3.6 { - execsql { COMMIT } -} {} -if {[atomic_batch_write test.db]==0} { - do_test wal2-6.3.6.1 { - list [file exists test.db-wal] [file exists test.db-journal] - } {0 1} -} -do_test wal2-6.3.7 { - execsql { PRAGMA lock_status } -} {main exclusive temp closed} -db close - - -# This test - wal2-6.4.* - uses a single database connection and the -# [testvfs] instrumentation to test that xShmLock() is being called -# as expected when a WAL database is used with locking_mode=exclusive. -# -do_test wal2-6.4.1 { - forcedelete test.db test.db-wal test.db-journal - proc tvfs_cb {method args} { - set ::shm_file [lindex $args 0] - if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] } - return "SQLITE_OK" - } - testvfs tvfs - tvfs script tvfs_cb - sqlite3 db test.db -vfs tvfs - set {} {} -} {} - -set RECOVERY { - {0 1 lock exclusive} {1 2 lock exclusive} - {4 1 lock exclusive} {4 1 unlock exclusive} - {5 1 lock exclusive} {5 1 unlock exclusive} - {6 1 lock exclusive} {6 1 unlock exclusive} - {7 1 lock exclusive} {7 1 unlock exclusive} - {1 2 unlock exclusive} {0 1 unlock exclusive} -} -set READMARK0_READ { - {3 1 lock shared} {3 1 unlock shared} -} -set READMARK0_WRITE { - {3 1 lock shared} - {0 1 lock exclusive} {3 1 unlock shared} - {4 1 lock exclusive} {4 1 unlock exclusive} {4 1 lock shared} - {0 1 unlock exclusive} {4 1 unlock shared} -} -set READMARK1_SET { - {4 1 lock exclusive} {4 1 unlock exclusive} -} -set READMARK1_READ { - {4 1 lock shared} {4 1 unlock shared} -} -set READMARK1_WRITE { - {4 1 lock shared} - {0 1 lock exclusive} {0 1 unlock exclusive} - {4 1 unlock shared} -} - -foreach {tn sql res expected_locks} { - 2 { - PRAGMA auto_vacuum = 0; - PRAGMA journal_mode = WAL; - BEGIN; - CREATE TABLE t1(x); - INSERT INTO t1 VALUES('Leonard'); - INSERT INTO t1 VALUES('Arthur'); - COMMIT; - } {wal} { - $RECOVERY - $READMARK0_WRITE - } - - 3 { - # This test should do the READMARK1_SET locking to populate the - # aReadMark[1] slot with the current mxFrame value. Followed by - # READMARK1_READ to read the database. - # - SELECT * FROM t1 - } {Leonard Arthur} { - $READMARK1_SET - $READMARK1_READ - } - - 4 { - # aReadMark[1] is already set to mxFrame. So just READMARK1_READ - # this time, not READMARK1_SET. - # - SELECT * FROM t1 ORDER BY x - } {Arthur Leonard} { - $READMARK1_READ - } - - 5 { - PRAGMA locking_mode = exclusive - } {exclusive} { } - - 6 { - INSERT INTO t1 VALUES('Julius Henry'); - SELECT * FROM t1; - } {Leonard Arthur {Julius Henry}} { - $READMARK1_READ - } - - 7 { - INSERT INTO t1 VALUES('Karl'); - SELECT * FROM t1; - } {Leonard Arthur {Julius Henry} Karl} { } - - 8 { - PRAGMA locking_mode = normal - } {normal} { } - - 9 { - SELECT * FROM t1 ORDER BY x - } {Arthur {Julius Henry} Karl Leonard} $READMARK1_READ - - 10 { DELETE FROM t1 } {} $READMARK1_WRITE - - 11 { - SELECT * FROM t1 - } {} { - $READMARK1_SET - $READMARK1_READ - } -} { - - set L [list] - foreach el [subst $expected_locks] { lappend L $el } - - set S "" - foreach sq [split $sql "\n"] { - set sq [string trim $sq] - if {[string match {#*} $sq]==0} {append S "$sq\n"} - } - - set ::locks [list] - do_test wal2-6.4.$tn.1 { execsql $S } $res - do_test wal2-6.4.$tn.2 { set ::locks } $L -} - -db close -tvfs delete - -do_test wal2-6.5.1 { - sqlite3 db test.db - execsql { - PRAGMA auto_vacuum = 0; - PRAGMA journal_mode = wal; - PRAGMA locking_mode = exclusive; - CREATE TABLE t2(a, b); - PRAGMA wal_checkpoint; - INSERT INTO t2 VALUES('I', 'II'); - PRAGMA journal_mode; - } -} {wal exclusive 0 2 2 wal} -do_test wal2-6.5.2 { - execsql { - PRAGMA locking_mode = normal; - INSERT INTO t2 VALUES('III', 'IV'); - PRAGMA locking_mode = exclusive; - SELECT * FROM t2; - } -} {normal exclusive I II III IV} -do_test wal2-6.5.3 { - execsql { PRAGMA wal_checkpoint } -} {0 2 2} -db close - -proc lock_control {method filename handle spec} { - foreach {start n op type} $spec break - if {$op == "lock"} { return SQLITE_IOERR } - return SQLITE_OK -} -do_test wal2-6.6.1 { - testvfs T - T script lock_control - T filter {} - sqlite3 db test.db -vfs T - execsql { SELECT * FROM sqlite_master } - execsql { PRAGMA locking_mode = exclusive } - execsql { INSERT INTO t2 VALUES('V', 'VI') } -} {} -do_test wal2-6.6.2 { - execsql { PRAGMA locking_mode = normal } - T filter xShmLock - execsql { INSERT INTO t2 VALUES('VII', 'VIII') } -} {} -do_test wal2-6.6.3 { - # At this point the connection should still be in exclusive-mode, even - # though it tried to exit exclusive-mode when committing the INSERT - # statement above. To exit exclusive mode, SQLite has to take a read-lock - # on the WAL file using xShmLock(). Since that call failed, it remains - # in exclusive mode. - # - sqlite3 db2 test.db -vfs T - catchsql { SELECT * FROM t2 } db2 -} {1 {database is locked}} -do_test wal2-6.6.2 { - db2 close - T filter {} - execsql { INSERT INTO t2 VALUES('IX', 'X') } -} {} -do_test wal2-6.6.4 { - # This time, we have successfully exited exclusive mode. So the second - # connection can read the database. - sqlite3 db2 test.db -vfs T - catchsql { SELECT * FROM t2 } db2 -} {0 {I II III IV V VI VII VIII IX X}} - -db close -db2 close -T delete - -#------------------------------------------------------------------------- -# Test a theory about the checksum algorithm. Theory was false and this -# test did not provoke a bug. -# -forcedelete test.db test.db-wal test.db-journal -do_test wal2-7.1.1 { - sqlite3 db test.db - execsql { - PRAGMA page_size = 4096; - PRAGMA journal_mode = WAL; - CREATE TABLE t1(a, b); - } - file size test.db -} {4096} -do_test wal2-7.1.2 { - forcecopy test.db test2.db - forcecopy test.db-wal test2.db-wal - # The first 32 bytes of the WAL file contain the WAL header. Offset 48 - # is the first byte of the checksum for the first frame in the WAL. - # The following three lines replaces the contents of that byte with - # a different value. - set newval FF - if {$newval == [hexio_read test2.db-wal 48 1]} { set newval 00 } - hexio_write test2.db-wal 48 $newval -} {1} -do_test wal2-7.1.3 { - sqlite3 db2 test2.db - execsql { PRAGMA wal_checkpoint } db2 - execsql { SELECT * FROM sqlite_master } db2 -} {} -db close -db2 close -forcedelete test.db test.db-wal test.db-journal -do_test wal2-8.1.2 { - sqlite3 db test.db - execsql { - PRAGMA auto_vacuum=OFF; - PRAGMA page_size = 1024; - PRAGMA journal_mode = WAL; - CREATE TABLE t1(x); - INSERT INTO t1 VALUES(zeroblob(8188*1020)); - CREATE TABLE t2(y); - PRAGMA wal_checkpoint; - } - execsql { - SELECT rootpage>=8192 FROM sqlite_master WHERE tbl_name = 't2'; - } -} {1} -do_test wal2-8.1.3 { - execsql { - PRAGMA cache_size = 10; - CREATE TABLE t3(z); - BEGIN; - INSERT INTO t3 VALUES(randomblob(900)); - INSERT INTO t3 SELECT randomblob(900) FROM t3; - INSERT INTO t2 VALUES('hello'); - INSERT INTO t3 SELECT randomblob(900) FROM t3; - INSERT INTO t3 SELECT randomblob(900) FROM t3; - INSERT INTO t3 SELECT randomblob(900) FROM t3; - INSERT INTO t3 SELECT randomblob(900) FROM t3; - INSERT INTO t3 SELECT randomblob(900) FROM t3; - INSERT INTO t3 SELECT randomblob(900) FROM t3; - ROLLBACK; - } - execsql { - INSERT INTO t2 VALUES('goodbye'); - INSERT INTO t3 SELECT randomblob(900) FROM t3; - INSERT INTO t3 SELECT randomblob(900) FROM t3; - } -} {} -do_test wal2-8.1.4 { - sqlite3 db2 test.db - execsql { SELECT * FROM t2 } -} {goodbye} -db2 close -db close - -#------------------------------------------------------------------------- -# Test that even if the checksums for both are valid, if the two copies -# of the wal-index header in the wal-index do not match, the client -# runs (or at least tries to run) database recovery. -# -# -proc get_name {method args} { set ::filename [lindex $args 0] ; tvfs filter {} } -testvfs tvfs -tvfs script get_name -tvfs filter xShmOpen - -forcedelete test.db test.db-wal test.db-journal -do_test wal2-9.1 { - sqlite3 db test.db -vfs tvfs - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE x(y); - INSERT INTO x VALUES('Barton'); - INSERT INTO x VALUES('Deakin'); - } - - # Set $wih(1) to the contents of the wal-index header after - # the frames associated with the first two rows in table 'x' have - # been inserted. Then insert one more row and set $wih(2) - # to the new value of the wal-index header. - # - # If the $wih(1) is written into the wal-index before running - # a read operation, the client will see only the first two rows. If - # $wih(2) is written into the wal-index, the client will see - # three rows. If an invalid header is written into the wal-index, then - # the client will run recovery and see three rows. - # - set wih(1) [set_tvfs_hdr $::filename] - execsql { INSERT INTO x VALUES('Watson') } - set wih(2) [set_tvfs_hdr $::filename] - - sqlite3 db2 test.db -vfs tvfs - execsql { SELECT * FROM x } db2 -} {Barton Deakin Watson} - -foreach {tn hdr1 hdr2 res} [list \ - 3 $wih(1) $wih(1) {Barton Deakin} \ - 4 $wih(1) $wih(2) {Barton Deakin Watson} \ - 5 $wih(2) $wih(1) {Barton Deakin Watson} \ - 6 $wih(2) $wih(2) {Barton Deakin Watson} \ - 7 $wih(1) $wih(1) {Barton Deakin} \ - 8 {0 0 0 0 0 0 0 0 0 0 0 0} {0 0 0 0 0 0 0 0 0 0 0 0} {Barton Deakin Watson} -] { - do_test wal2-9.$tn { - set_tvfs_hdr $::filename $hdr1 $hdr2 - execsql { SELECT * FROM x } db2 - } $res -} - -db2 close -db close - -#------------------------------------------------------------------------- -# This block of tests - wal2-10.* - focus on the libraries response to -# new versions of the wal or wal-index formats. -# -# wal2-10.1.*: Test that the library refuses to "recover" a new WAL -# format. -# -# wal2-10.2.*: Test that the library refuses to read or write a database -# if the wal-index version is newer than it understands. -# -# At time of writing, the only versions of the wal and wal-index formats -# that exist are versions 3007000 (corresponding to SQLite version 3.7.0, -# the first version of SQLite to feature wal mode). -# -do_test wal2-10.1.1 { - faultsim_delete_and_reopen - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE t1(a, b); - PRAGMA wal_checkpoint; - INSERT INTO t1 VALUES(1, 2); - INSERT INTO t1 VALUES(3, 4); - } - faultsim_save_and_close -} {} -do_test wal2-10.1.2 { - faultsim_restore_and_reopen - execsql { SELECT * FROM t1 } -} {1 2 3 4} -do_test wal2-10.1.3 { - faultsim_restore_and_reopen - set hdr [wal_set_walhdr test.db-wal] - lindex $hdr 1 -} {3007000} -do_test wal2-10.1.4 { - lset hdr 1 3007001 - wal_set_walhdr test.db-wal $hdr - catchsql { SELECT * FROM t1 } -} {1 {unable to open database file}} - -testvfs tvfs -default 1 -do_test wal2-10.2.1 { - faultsim_restore_and_reopen - execsql { SELECT * FROM t1 } -} {1 2 3 4} -do_test wal2-10.2.2 { - set hdr [set_tvfs_hdr $::filename] - lindex $hdr 0 -} {3007000} -do_test wal2-10.2.3 { - lset hdr 0 3007001 - wal_fix_walindex_cksum hdr - set_tvfs_hdr $::filename $hdr - catchsql { SELECT * FROM t1 } -} {1 {unable to open database file}} -db close -tvfs delete - -#------------------------------------------------------------------------- -# This block of tests - wal2-11.* - tests that it is not possible to put -# the library into an infinite loop by presenting it with a corrupt -# hash table (one that appears to contain a single chain of infinite -# length). -# -# wal2-11.1.*: While reading the hash-table. -# -# wal2-11.2.*: While writing the hash-table. -# -testvfs tvfs -default 1 -do_test wal2-11.0 { - faultsim_delete_and_reopen - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE t1(a, b, c); - INSERT INTO t1 VALUES(1, 2, 3); - INSERT INTO t1 VALUES(4, 5, 6); - INSERT INTO t1 VALUES(7, 8, 9); - SELECT * FROM t1; - } -} {wal 1 2 3 4 5 6 7 8 9} - -do_test wal2-11.1.1 { - sqlite3 db2 test.db - execsql { SELECT name FROM sqlite_master } db2 -} {t1} - -if {$::tcl_version>=8.5} { - # Set all zeroed slots in the first hash table to invalid values. - # - set blob [string range [tvfs shm $::filename] 0 16383] - set I [string range [tvfs shm $::filename] 16384 end] - binary scan $I t* L - set I [list] - foreach p $L { - lappend I [expr $p ? $p : 400] - } - append blob [binary format t* $I] - tvfs shm $::filename $blob - do_test wal2-11.2 { - catchsql { INSERT INTO t1 VALUES(10, 11, 12) } - } {1 {database disk image is malformed}} - - # Fill up the hash table on the first page of shared memory with 0x55 bytes. - # - set blob [string range [tvfs shm $::filename] 0 16383] - append blob [string repeat [binary format c 55] 16384] - tvfs shm $::filename $blob - do_test wal2-11.3 { - catchsql { SELECT * FROM t1 } db2 - } {1 {database disk image is malformed}} -} - -db close -db2 close -tvfs delete - -#------------------------------------------------------------------------- -# If a connection is required to create a WAL or SHM file, it creates -# the new files with the same file-system permissions as the database -# file itself. Test this. -# -if {$::tcl_platform(platform) == "unix"} { - faultsim_delete_and_reopen - # Changed on 2012-02-13: umask is deliberately ignored for -wal files. - #set umask [exec /bin/sh -c umask] - set umask 0 - - - do_test wal2-12.1 { - sqlite3 db test.db - execsql { - CREATE TABLE tx(y, z); - PRAGMA journal_mode = WAL; - } - db close - list [file exists test.db-wal] [file exists test.db-shm] - } {0 0} - - foreach {tn permissions} { - 1 00644 - 2 00666 - 3 00600 - 4 00755 - } { - set effective [format %.5o [expr $permissions & ~$umask]] - do_test wal2-12.2.$tn.1 { - file attributes test.db -permissions $permissions - string map {o 0} [file attributes test.db -permissions] - } $permissions - do_test wal2-12.2.$tn.2 { - list [file exists test.db-wal] [file exists test.db-shm] - } {0 0} - do_test wal2-12.2.$tn.3 { - sqlite3 db test.db - execsql { INSERT INTO tx DEFAULT VALUES } - list [file exists test.db-wal] [file exists test.db-shm] - } {1 1} - do_test wal2-12.2.$tn.4 { - set x [list [file attr test.db-wal -perm] [file attr test.db-shm -perm]] - string map {o 0} $x - } [list $effective $effective] - do_test wal2-12.2.$tn.5 { - db close - list [file exists test.db-wal] [file exists test.db-shm] - } {0 0} - } -} - -#------------------------------------------------------------------------- -# Test the libraries response to discovering that one or more of the -# database, wal or shm files cannot be opened, or can only be opened -# read-only. -# -if {$::tcl_platform(platform) == "unix"} { - proc perm {} { - set L [list] - foreach f {test.db test.db-wal test.db-shm} { - if {[file exists $f]} { - lappend L [file attr $f -perm] - } else { - lappend L {} - } - } - set L - } - - faultsim_delete_and_reopen - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE t1(a, b); - PRAGMA wal_checkpoint; - INSERT INTO t1 VALUES('3.14', '2.72'); - } - do_test wal2-13.1.1 { - list [file exists test.db-shm] [file exists test.db-wal] - } {1 1} - faultsim_save_and_close - - foreach {tn db_perm wal_perm shm_perm can_open can_read can_write} { - 2 00644 00644 00644 1 1 1 - 3 00644 00400 00644 1 1 0 - 4 00644 00644 00400 1 1 0 - 5 00400 00644 00644 1 1 0 - - 7 00644 00000 00644 1 0 0 - 8 00644 00644 00000 1 0 0 - 9 00000 00644 00644 0 0 0 - } { - faultsim_restore - do_test wal2-13.$tn.1 { - file attr test.db -perm $db_perm - file attr test.db-wal -perm $wal_perm - file attr test.db-shm -perm $shm_perm - - set L [file attr test.db -perm] - lappend L [file attr test.db-wal -perm] - lappend L [file attr test.db-shm -perm] - string map {o 0} $L - } [list $db_perm $wal_perm $shm_perm] - - # If $can_open is true, then it should be possible to open a database - # handle. Otherwise, if $can_open is 0, attempting to open the db - # handle throws an "unable to open database file" exception. - # - set r(1) {0 ok} - set r(0) {1 {unable to open database file}} - do_test wal2-13.$tn.2 { - list [catch {sqlite3 db test.db ; set {} ok} msg] $msg - } $r($can_open) - - if {$can_open} { - - # If $can_read is true, then the client should be able to read from - # the database file. If $can_read is false, attempting to read should - # throw the "unable to open database file" exception. - # - set a(0) {1 {unable to open database file}} - set a(1) {0 {3.14 2.72}} - do_test wal2-13.$tn.3 { - catchsql { SELECT * FROM t1 } - } $a($can_read) - - # Now try to write to the db file. If the client can read but not - # write, then it should throw the familiar "unable to open db file" - # exception. If it can read but not write, the exception should - # be "attempt to write a read only database". - # - # If the client can read and write, the operation should succeed. - # - set b(0,0) {1 {unable to open database file}} - set b(1,0) {1 {attempt to write a readonly database}} - set b(1,1) {0 {}} - do_test wal2-13.$tn.4 { - catchsql { INSERT INTO t1 DEFAULT VALUES } - } $b($can_read,$can_write) - } - catch { db close } - } -} - -#------------------------------------------------------------------------- -# Test that "PRAGMA checkpoint_fullsync" appears to be working. -# -foreach {tn sql reslist} { - 1 { } {10 0 4 0 6 0} - 2 { PRAGMA checkpoint_fullfsync = 1 } {10 6 4 3 6 3} - 3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0} -} { - ifcapable default_ckptfullfsync { - if {[string trim $sql]==""} continue - } - faultsim_delete_and_reopen - - execsql {PRAGMA auto_vacuum = 0; PRAGMA synchronous = FULL;} - execsql $sql - do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 } {} - do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal} - - set sqlite_sync_count 0 - set sqlite_fullsync_count 0 - - do_execsql_test wal2-14.$tn.2 { - PRAGMA wal_autocheckpoint = 10; - CREATE TABLE t1(a, b); -- 2 wal syncs - INSERT INTO t1 VALUES(1, 2); -- 2 wal sync - PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync - BEGIN; - INSERT INTO t1 VALUES(3, 4); - INSERT INTO t1 VALUES(5, 6); - COMMIT; -- 2 wal sync - PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync - } {10 0 3 3 0 1 1} - - do_test wal2-14.$tn.3 { - cond_incr_sync_count 1 - list $sqlite_sync_count $sqlite_fullsync_count - } [lrange $reslist 0 1] - - set sqlite_sync_count 0 - set sqlite_fullsync_count 0 - - do_test wal2-14.$tn.4 { - execsql { INSERT INTO t1 VALUES(7, zeroblob(12*4096)) } - list $sqlite_sync_count $sqlite_fullsync_count - } [lrange $reslist 2 3] - - set sqlite_sync_count 0 - set sqlite_fullsync_count 0 - - do_test wal2-14.$tn.5 { - execsql { PRAGMA wal_autocheckpoint = 1000 } - execsql { INSERT INTO t1 VALUES(9, 10) } - execsql { INSERT INTO t1 VALUES(11, 12) } - execsql { INSERT INTO t1 VALUES(13, 14) } - db close - list $sqlite_sync_count $sqlite_fullsync_count - } [lrange $reslist 4 5] -} - -catch { db close } - -# PRAGMA checkpoint_fullsync -# PRAGMA fullfsync -# PRAGMA synchronous -# -foreach {tn settings restart_sync commit_sync ckpt_sync} { - 1 {0 0 off} {0 0} {0 0} {0 0} - 2 {0 0 normal} {1 0} {0 0} {2 0} - 3 {0 0 full} {2 0} {1 0} {2 0} - - 4 {0 1 off} {0 0} {0 0} {0 0} - 5 {0 1 normal} {0 1} {0 0} {0 2} - 6 {0 1 full} {0 2} {0 1} {0 2} - - 7 {1 0 off} {0 0} {0 0} {0 0} - 8 {1 0 normal} {0 1} {0 0} {0 2} - 9 {1 0 full} {1 1} {1 0} {0 2} - - 10 {1 1 off} {0 0} {0 0} {0 0} - 11 {1 1 normal} {0 1} {0 0} {0 2} - 12 {1 1 full} {0 2} {0 1} {0 2} -} { - forcedelete test.db - - testvfs tvfs -default 1 - tvfs filter xSync - tvfs script xSyncCb - proc xSyncCb {method file fileid flags} { - incr ::sync($flags) - } - - sqlite3 db test.db - do_execsql_test 15.$tn.1 " - PRAGMA page_size = 4096; - CREATE TABLE t1(x); - PRAGMA wal_autocheckpoint = OFF; - PRAGMA journal_mode = WAL; - PRAGMA checkpoint_fullfsync = [lindex $settings 0]; - PRAGMA fullfsync = [lindex $settings 1]; - PRAGMA synchronous = [lindex $settings 2]; - " {0 wal} - - do_test 15.$tn.2 { - set sync(normal) 0 - set sync(full) 0 - execsql { INSERT INTO t1 VALUES('abc') } - list $::sync(normal) $::sync(full) - } $restart_sync - - do_test 15.$tn.3 { - set sync(normal) 0 - set sync(full) 0 - execsql { INSERT INTO t1 VALUES('abc') } - list $::sync(normal) $::sync(full) - } $commit_sync - - do_test 15.$tn.4 { - set sync(normal) 0 - set sync(full) 0 - execsql { INSERT INTO t1 VALUES('def') } - list $::sync(normal) $::sync(full) - } $commit_sync - - do_test 15.$tn.5 { - set sync(normal) 0 - set sync(full) 0 - execsql { PRAGMA wal_checkpoint } - list $::sync(normal) $::sync(full) - } $ckpt_sync - - db close - tvfs delete -} - - - -finish_test |
