diff options
Diffstat (limited to 'test/exclusive2.test')
-rw-r--r-- | test/exclusive2.test | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/test/exclusive2.test b/test/exclusive2.test new file mode 100644 index 0000000..2208da5 --- /dev/null +++ b/test/exclusive2.test @@ -0,0 +1,317 @@ +# 2007 March 24 +# +# 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. +# +# $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Do not use a codec for tests in this file, as the database file is +# manipulated directly using tcl scripts (using the [hexio_write] command). +# +do_not_use_codec + +ifcapable {!pager_pragmas} { + finish_test + return +} + +# This module does not work right if the cache spills at unexpected +# moments. So disable the soft-heap-limit. +# +sqlite3_soft_heap_limit 0 + +proc pagerChangeCounter {filename new {fd ""}} { + if {$fd==""} { + set fd [open $filename RDWR] + fconfigure $fd -translation binary -encoding binary + set needClose 1 + } else { + set needClose 0 + } + if {$new ne ""} { + seek $fd 24 + set a [expr {($new&0xFF000000)>>24}] + set b [expr {($new&0x00FF0000)>>16}] + set c [expr {($new&0x0000FF00)>>8}] + set d [expr {($new&0x000000FF)}] + puts -nonewline $fd [binary format cccc $a $b $c $d] + flush $fd + } + + seek $fd 24 + foreach {a b c d} [list 0 0 0 0] {} + binary scan [read $fd 4] cccc a b c d + set ret [expr ($a&0x000000FF)<<24] + incr ret [expr ($b&0x000000FF)<<16] + incr ret [expr ($c&0x000000FF)<<8] + incr ret [expr ($d&0x000000FF)<<0] + + if {$needClose} {close $fd} + return $ret +} + +proc readPagerChangeCounter {filename} { + set fd [open $filename RDONLY] + fconfigure $fd -translation binary -encoding binary + + seek $fd 24 + foreach {a b c d} [list 0 0 0 0] {} + binary scan [read $fd 4] cccc a b c d + set ret [expr ($a&0x000000FF)<<24] + incr ret [expr ($b&0x000000FF)<<16] + incr ret [expr ($c&0x000000FF)<<8] + incr ret [expr ($d&0x000000FF)<<0] + + close $fd + return $ret +} + + +proc t1sig {{db db}} { + execsql {SELECT count(*), md5sum(a) FROM t1} $db +} +do_test exclusive2-1.0 { + readPagerChangeCounter test.db +} {0} + +#----------------------------------------------------------------------- +# The following tests - exclusive2-1.X - check that: +# +# 1-3: Build a database with connection 1, calculate a signature. +# 4-7: Modify the database using a second connection in a way that +# does not modify the freelist, then reset the pager change-counter +# to the value it had before the modifications. +# 8: Check that using the first connection, the database signature +# is still the same. This is because it uses the in-memory cache. +# It can't tell the db has changed because we reset the change-counter. +# 9: Increment the change-counter. +# 10: Ensure that the first connection now sees the updated database. It +# sees the change-counter has been incremented and discards the +# invalid in-memory cache. +# +# This will only work if the database cache is large enough to hold +# the entire database. In the case of 1024 byte pages, this means +# the cache size must be at least 17. Otherwise, some pages will be +# loaded from the database file in step 8. +# +# For similar reasons, this test does not work with the memsubsys1 permutation. +# Permutation memsubsys1 configures the pcache subsystem to use a static +# allocation of 24 pages (shared between all pagers). This is not enough for +# this test. +# +do_test exclusive2-1.1 { + execsql { + BEGIN; + CREATE TABLE t1(a, b); + INSERT INTO t1(a) VALUES(randstr(10, 400)); + INSERT INTO t1(a) VALUES(randstr(10, 400)); + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + COMMIT; + SELECT count(*) FROM t1; + } +} {64} +do_test exclusive2-1.2.1 { + # Make sure the pager cache is large enough to store the + # entire database. + set nPage [expr [file size test.db]/1024] + if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { + execsql "PRAGMA cache_size = $nPage" + } + expr {[execsql {PRAGMA cache_size}] >= $nPage} +} {1} +do_test exclusive2-1.2 { + set ::sig [t1sig] + readPagerChangeCounter test.db +} {1} +do_test exclusive2-1.3 { + t1sig +} $::sig +do_test exclusive2-1.4 { + sqlite3 db2 test.db + t1sig db2 +} $::sig +do_test exclusive2-1.5 { + execsql { + UPDATE t1 SET b=a, a=NULL; + } db2 + expr {[t1sig db2] eq $::sig} +} 0 +do_test exclusive2-1.6 { + readPagerChangeCounter test.db +} {2} +do_test exclusive2-1.7 { + pagerChangeCounter test.db 1 +} {1} +if {[permutation] != "memsubsys1"} { + do_test exclusive2-1.9 { + t1sig + expr {[t1sig] eq $::sig} + } {1} +} +do_test exclusive2-1.10 { + pagerChangeCounter test.db 2 +} {2} +do_test exclusive2-1.11 { + expr {[t1sig] eq $::sig} +} {0} +db2 close + +#-------------------------------------------------------------------- +# These tests - exclusive2-2.X - are similar to exclusive2-1.X, +# except that they are run with locking_mode=EXCLUSIVE. +# +# 1-3: Build a database with exclusive-access connection 1, +# calculate a signature. +# 4: Corrupt the database by writing 10000 bytes of garbage +# starting at the beginning of page 2. Check that connection 1 +# still works. It should be accessing the in-memory cache. +# 5-6: Modify the dataase change-counter. Connection 1 still works +# entirely from in-memory cache, because it doesn't check the +# change-counter. +# 7-8 Set the locking-mode back to normal. After the db is unlocked, +# SQLite detects the modified change-counter and discards the +# in-memory cache. Then it finds the corruption caused in step 4.... +# +# As above, this test is only applicable if the pager cache is +# large enough to hold the entire database. With 1024 byte pages, +# this means 19 pages. We also need to disable the soft-heap-limit +# to prevent memory-induced cache spills. +# +do_test exclusive2-2.1 { + execsql {PRAGMA cache_size=1000;} + execsql {PRAGMA locking_mode = exclusive;} + execsql { + BEGIN; + DELETE FROM t1; + INSERT INTO t1(a) VALUES(randstr(10, 400)); + INSERT INTO t1(a) VALUES(randstr(10, 400)); + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + COMMIT; + SELECT count(*) FROM t1; + } +} {64} +do_test exclusive2-2.2.1 { + # Make sure the pager cache is large enough to store the + # entire database. + set nPage [expr [file size test.db]/1024] + if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { + execsql "PRAGMA cache_size = $nPage" + } + expr {[execsql {PRAGMA cache_size}] >= $nPage} +} {1} +do_test exclusive2-2.2 { + set ::sig [t1sig] + readPagerChangeCounter test.db +} {3} +do_test exclusive2-2.3 { + t1sig +} $::sig + +do_test exclusive2-2.4 { + set ::fd [open test.db RDWR] + fconfigure $::fd -translation binary + seek $::fd 1024 + puts -nonewline $::fd [string repeat [binary format c 0] 10000] + flush $::fd + t1sig +} $::sig + +do_test exclusive2-2.5 { + pagerChangeCounter test.db 5 $::fd +} {5} +do_test exclusive2-2.6 { + t1sig +} $::sig +do_test exclusive2-2.7 { + execsql {PRAGMA locking_mode = normal} + t1sig +} $::sig + +do_test exclusive2-2.8 { + set rc [catch {t1sig} msg] + list $rc $msg +} {1 {database disk image is malformed}} + +#-------------------------------------------------------------------- +# These tests - exclusive2-3.X - verify that the pager change-counter +# is only incremented by the first change when in exclusive access +# mode. In normal mode, the change-counter is incremented once +# per write-transaction. +# + +db close +catch {close $::fd} +forcedelete test.db +forcedelete test.db-journal + +do_test exclusive2-3.0 { + sqlite3 db test.db + execsql { + BEGIN; + CREATE TABLE t1(a UNIQUE); + INSERT INTO t1 VALUES(randstr(200, 200)); + INSERT INTO t1 VALUES(randstr(200, 200)); + COMMIT; + } + readPagerChangeCounter test.db +} {1} +do_test exclusive2-3.1 { + execsql { + INSERT INTO t1 VALUES(randstr(200, 200)); + } + readPagerChangeCounter test.db +} {2} +do_test exclusive2-3.2 { + execsql { + INSERT INTO t1 VALUES(randstr(200, 200)); + } + readPagerChangeCounter test.db +} {3} +do_test exclusive2-3.3 { + execsql { + PRAGMA locking_mode = exclusive; + INSERT INTO t1 VALUES(randstr(200, 200)); + } + readPagerChangeCounter test.db +} {4} +do_test exclusive2-3.4 { +breakpoint + execsql { + INSERT INTO t1 VALUES(randstr(200, 200)); + } + readPagerChangeCounter test.db +} {4} +do_test exclusive2-3.5 { + execsql { + PRAGMA locking_mode = normal; + INSERT INTO t1 VALUES(randstr(200, 200)); + } + readPagerChangeCounter test.db +} {4} +do_test exclusive2-3.6 { + execsql { + INSERT INTO t1 VALUES(randstr(200, 200)); + } + readPagerChangeCounter test.db +} {5} +sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) + +finish_test |