sqlite/test/wal2.test
dan ed36020daf Remove a branch made redundant by the earlier exclusive-mode changes.
FossilOrigin-Name: c501b2ede6aad123bef0aa7ce8b356a134eb6d26
2010-05-12 06:54:31 +00:00

595 lines
15 KiB
Plaintext

# 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
ifcapable !wal {finish_test ; return }
proc set_tvfs_hdr {file args} {
if {[llength $args]>1} {
return -code error {wrong # args: should be "set_tvfs_hdr fileName ?val?"}
}
set blob [tvfs shm $file]
if {[llength $args]} {
set blob [binary format i8a* [lindex $args 0] [string range $blob 32 end]]
tvfs shm $file $blob
}
binary scan $blob i8 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 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 args} { return SQLITE_OK }
testvfs tvfs tvfs_cb
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}
foreach {tn iInsert res wal_index_hdr_mod wal_locks} {
2 5 {5 15} 0 {READ RECOVER READ UNLOCK}
3 6 {6 21} 1 {READ RECOVER READ UNLOCK}
4 7 {7 28} 2 {READ RECOVER READ UNLOCK}
5 8 {8 36} 3 {READ RECOVER READ UNLOCK}
6 9 {9 45} 4 {READ RECOVER READ UNLOCK}
7 10 {10 55} 5 {READ RECOVER READ UNLOCK}
8 11 {11 66} 6 {READ RECOVER READ UNLOCK}
9 12 {12 78} 7 {READ RECOVER READ UNLOCK}
10 13 {13 91} -1 {READ UNLOCK}
} {
do_test wal2-1.$tn.1 {
execsql { INSERT INTO t1 VALUES($iInsert) }
set ::locks [list]
set ::cb_done 0
proc tvfs_cb {method args} {
if {$::cb_done == 0 && $method == "xShmGet"} {
set ::cb_done 1
if {$::wal_index_hdr_mod >= 0} {
incr_tvfs_hdr [lindex $args 0] $::wal_index_hdr_mod 1
}
}
if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] }
return SQLITE_OK
}
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
file delete -force 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.
#
do_test wal2-2.0 {
testvfs tvfs tvfs_cb
proc tvfs_cb {method args} {
if {$method == "xShmOpen"} { set ::shm_file [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
} {
do_test wal2-1.$tn.1 {
set oldhdr [set_tvfs_hdr $::shm_file]
execsql { INSERT INTO t1 VALUES($iInsert) }
execsql { SELECT count(a), sum(a) FROM t1 }
} $res1
do_test wal2-2.$tn.2 {
set ::locks [list]
set ::cb_done 0
proc tvfs_cb {method args} {
if {$::cb_done == 0 && $method == "xShmGet"} {
set ::cb_done 1
if {$::wal_index_hdr_mod >= 0} {
incr_tvfs_hdr $::shm_file $::wal_index_hdr_mod 1
}
}
if {$method == "xShmLock"} {
set lock [lindex $args 2]
lappend ::locks $lock
if {$lock == "RECOVER"} {
set_tvfs_hdr $::shm_file $::oldhdr
}
}
return SQLITE_OK
}
execsql { SELECT count(a), sum(a) FROM t1 } db2
} $res0
do_test wal2-2.$tn.3 {
set ::locks
} {READ RECOVER READ UNLOCK}
do_test wal2-2.$tn.4 {
set ::locks [list]
set ::cb_done 0
proc tvfs_cb {method args} {
if {$::cb_done == 0 && $method == "xShmGet"} {
set ::cb_done 1
if {$::wal_index_hdr_mod >= 0} {
incr_tvfs_hdr $::shm_file $::wal_index_hdr_mod 1
}
}
if {$method == "xShmLock"} {
set lock [lindex $args 2]
lappend ::locks $lock
}
return SQLITE_OK
}
execsql { SELECT count(a), sum(a) FROM t1 } db2
} $res1
}
db close
db2 close
tvfs delete
file delete -force test.db test.db-wal test.db-journal
#-------------------------------------------------------------------------
# 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_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
file delete -force 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 journal_mode = WAL;
CREATE TABLE data(x);
INSERT INTO data VALUES('need xShmOpen to see this');
PRAGMA wal_checkpoint;
}
} {wal}
do_test wal2-4.2 {
db close
proc ok {args} {return SQLITE_OK}
testvfs -noshm tvfs ok
sqlite3 db test.db -vfs tvfs
catchsql { SELECT * FROM data }
} {1 {unable to open database file}}
do_test wal2-4.3 {
db close
proc ok {args} {return SQLITE_OK}
testvfs tvfs ok
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.
#
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_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
} {CHECKPOINT UNLOCK}
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.
#
do_test wal2-6.1.1 {
file delete -force test.db test.db-wal test.db-journal
sqlite3 db test.db
execsql {
Pragma Journal_Mode = Wal;
Pragma Locking_Mode = Exclusive;
}
} {wal exclusive}
do_test wal2-6.1.2 {
execsql { PRAGMA lock_status }
} {main unlocked temp closed}
do_test wal2-6.1.3 {
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 exclusive 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 {
file delete -force 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 { 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 exclusive 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 {
file delete -force 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');
}
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 }
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
do_test wal2-6.4.1 {
file delete -force 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_cb
sqlite3 db test.db -vfs tvfs
execsql {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES('Leonard');
INSERT INTO t1 VALUES('Arthur');
}
set ::locks [list]
execsql { PRAGMA locking_mode = exclusive }
set ::locks
} {}
do_test wal2-6.4.2 {
execsql { SELECT * FROM t1 }
} {Leonard Arthur}
do_test wal2-6.4.3 {
set ::locks
} {READ}
do_test wal2-6.4.4 {
execsql {
INSERT INTO t1 VALUES('Julius Henry');
SELECT * FROM t1;
}
} {Leonard Arthur {Julius Henry}}
do_test wal2-6.4.5 {
set ::locks
} {READ}
do_test wal2-6.4.6 {
execsql {
PRAGMA locking_mode = NORMAL;
DELETE FROM t1;
}
set ::locks
} {READ UNLOCK}
do_test wal2-6.4.7 {
set ::locks [list]
execsql { INSERT INTO t1 VALUES('Karl') }
set ::locks
} {READ WRITE READ UNLOCK}
db close
tvfs delete
do_test wal2-6.5.1 {
sqlite3 db test.db
execsql {
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 wal}
db close
finish_test