1ded6f4b2Sdanielk1977# 2007 March 24 2ded6f4b2Sdanielk1977# 3ded6f4b2Sdanielk1977# The author disclaims copyright to this source code. In place of 4ded6f4b2Sdanielk1977# a legal notice, here is a blessing: 5ded6f4b2Sdanielk1977# 6ded6f4b2Sdanielk1977# May you do good and not evil. 7ded6f4b2Sdanielk1977# May you find forgiveness for yourself and forgive others. 8ded6f4b2Sdanielk1977# May you share freely, never taking more than you give. 9ded6f4b2Sdanielk1977# 10ded6f4b2Sdanielk1977#*********************************************************************** 11ded6f4b2Sdanielk1977# This file implements regression tests for SQLite library. 12ded6f4b2Sdanielk1977# 13c5053fb9Sdrh# $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $ 14ded6f4b2Sdanielk1977 15ded6f4b2Sdanielk1977set testdir [file dirname $argv0] 16ded6f4b2Sdanielk1977source $testdir/tester.tcl 17ded6f4b2Sdanielk1977 18ae23162eSshaneh# Do not use a codec for tests in this file, as the database file is 19ae23162eSshaneh# manipulated directly using tcl scripts (using the [hexio_write] command). 20ae23162eSshaneh# 21ae23162eSshanehdo_not_use_codec 22ae23162eSshaneh 23ded6f4b2Sdanielk1977ifcapable {!pager_pragmas} { 24ded6f4b2Sdanielk1977 finish_test 25ded6f4b2Sdanielk1977 return 26ded6f4b2Sdanielk1977} 27ded6f4b2Sdanielk1977 285d8a1372Sdan# Tests in this file verify that locking_mode=exclusive causes SQLite to 295d8a1372Sdan# use cached pages even if the database is changed on disk. This doesn't 305d8a1372Sdan# work with mmap. 319b4c59faSdrhif {[permutation]=="mmap"} { 325d8a1372Sdan finish_test 335d8a1372Sdan return 345d8a1372Sdan} 355d8a1372Sdan 363aefabafSdrh# This module does not work right if the cache spills at unexpected 373aefabafSdrh# moments. So disable the soft-heap-limit. 383aefabafSdrh# 393aefabafSdrhsqlite3_soft_heap_limit 0 403aefabafSdrh 41831045ddSaswiftproc pagerChangeCounter {filename new {fd ""}} { 42831045ddSaswift if {$fd==""} { 43e6895112Sdrh set fd [open $filename RDWR] 44ded6f4b2Sdanielk1977 fconfigure $fd -translation binary -encoding binary 45831045ddSaswift set needClose 1 46831045ddSaswift } else { 47831045ddSaswift set needClose 0 48831045ddSaswift } 49ded6f4b2Sdanielk1977 if {$new ne ""} { 50ded6f4b2Sdanielk1977 seek $fd 24 51ded6f4b2Sdanielk1977 set a [expr {($new&0xFF000000)>>24}] 52ded6f4b2Sdanielk1977 set b [expr {($new&0x00FF0000)>>16}] 53ded6f4b2Sdanielk1977 set c [expr {($new&0x0000FF00)>>8}] 54ded6f4b2Sdanielk1977 set d [expr {($new&0x000000FF)}] 553fb79c83Sdanielk1977 puts -nonewline $fd [binary format cccc $a $b $c $d] 563fb79c83Sdanielk1977 flush $fd 57ded6f4b2Sdanielk1977 } 58ded6f4b2Sdanielk1977 59ded6f4b2Sdanielk1977 seek $fd 24 60ded6f4b2Sdanielk1977 foreach {a b c d} [list 0 0 0 0] {} 61ded6f4b2Sdanielk1977 binary scan [read $fd 4] cccc a b c d 62ded6f4b2Sdanielk1977 set ret [expr ($a&0x000000FF)<<24] 63ded6f4b2Sdanielk1977 incr ret [expr ($b&0x000000FF)<<16] 64ded6f4b2Sdanielk1977 incr ret [expr ($c&0x000000FF)<<8] 65ded6f4b2Sdanielk1977 incr ret [expr ($d&0x000000FF)<<0] 66ded6f4b2Sdanielk1977 67831045ddSaswift if {$needClose} {close $fd} 68ded6f4b2Sdanielk1977 return $ret 69ded6f4b2Sdanielk1977} 70ded6f4b2Sdanielk1977 71f6f426beSaswiftproc readPagerChangeCounter {filename} { 72f6f426beSaswift set fd [open $filename RDONLY] 73f6f426beSaswift fconfigure $fd -translation binary -encoding binary 74f6f426beSaswift 75f6f426beSaswift seek $fd 24 76f6f426beSaswift foreach {a b c d} [list 0 0 0 0] {} 77f6f426beSaswift binary scan [read $fd 4] cccc a b c d 78f6f426beSaswift set ret [expr ($a&0x000000FF)<<24] 79f6f426beSaswift incr ret [expr ($b&0x000000FF)<<16] 80f6f426beSaswift incr ret [expr ($c&0x000000FF)<<8] 81f6f426beSaswift incr ret [expr ($d&0x000000FF)<<0] 82f6f426beSaswift 83f6f426beSaswift close $fd 84f6f426beSaswift return $ret 85f6f426beSaswift} 86f6f426beSaswift 87f6f426beSaswift 88ded6f4b2Sdanielk1977proc t1sig {{db db}} { 89ded6f4b2Sdanielk1977 execsql {SELECT count(*), md5sum(a) FROM t1} $db 90ded6f4b2Sdanielk1977} 91ded6f4b2Sdanielk1977do_test exclusive2-1.0 { 92f6f426beSaswift readPagerChangeCounter test.db 93ded6f4b2Sdanielk1977} {0} 94ded6f4b2Sdanielk1977 95ded6f4b2Sdanielk1977#----------------------------------------------------------------------- 96ded6f4b2Sdanielk1977# The following tests - exclusive2-1.X - check that: 97ded6f4b2Sdanielk1977# 98ded6f4b2Sdanielk1977# 1-3: Build a database with connection 1, calculate a signature. 99fa2a4772Sdan# 4-7: Modify the database using a second connection in a way that 10086a88114Sdrh# does not modify the freelist, then reset the pager change-counter 10186a88114Sdrh# to the value it had before the modifications. 102ded6f4b2Sdanielk1977# 8: Check that using the first connection, the database signature 103ded6f4b2Sdanielk1977# is still the same. This is because it uses the in-memory cache. 104ded6f4b2Sdanielk1977# It can't tell the db has changed because we reset the change-counter. 105ded6f4b2Sdanielk1977# 9: Increment the change-counter. 106ded6f4b2Sdanielk1977# 10: Ensure that the first connection now sees the updated database. It 107ded6f4b2Sdanielk1977# sees the change-counter has been incremented and discards the 108ded6f4b2Sdanielk1977# invalid in-memory cache. 109ded6f4b2Sdanielk1977# 11068a6b5ecSdanielk1977# This will only work if the database cache is large enough to hold 11168a6b5ecSdanielk1977# the entire database. In the case of 1024 byte pages, this means 11268a6b5ecSdanielk1977# the cache size must be at least 17. Otherwise, some pages will be 11368a6b5ecSdanielk1977# loaded from the database file in step 8. 11468a6b5ecSdanielk1977# 115fa2a4772Sdan# For similar reasons, this test does not work with the memsubsys1 permutation. 116fa2a4772Sdan# Permutation memsubsys1 configures the pcache subsystem to use a static 117fa2a4772Sdan# allocation of 24 pages (shared between all pagers). This is not enough for 118fa2a4772Sdan# this test. 119fa2a4772Sdan# 120ded6f4b2Sdanielk1977do_test exclusive2-1.1 { 121ded6f4b2Sdanielk1977 execsql { 122ded6f4b2Sdanielk1977 BEGIN; 12386a88114Sdrh CREATE TABLE t1(a, b); 124*d47e1ccbSdan INSERT INTO t1(a, b) VALUES(randstr(10, 400), 0); 125*d47e1ccbSdan INSERT INTO t1(a, b) VALUES(randstr(10, 400), 0); 126*d47e1ccbSdan INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; 127*d47e1ccbSdan INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; 128*d47e1ccbSdan INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; 129*d47e1ccbSdan INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; 130*d47e1ccbSdan INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; 131ded6f4b2Sdanielk1977 COMMIT; 132ded6f4b2Sdanielk1977 SELECT count(*) FROM t1; 133ded6f4b2Sdanielk1977 } 134ded6f4b2Sdanielk1977} {64} 13568a6b5ecSdanielk1977do_test exclusive2-1.2.1 { 13668a6b5ecSdanielk1977 # Make sure the pager cache is large enough to store the 13768a6b5ecSdanielk1977 # entire database. 13868a6b5ecSdanielk1977 set nPage [expr [file size test.db]/1024] 13968a6b5ecSdanielk1977 if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { 14068a6b5ecSdanielk1977 execsql "PRAGMA cache_size = $nPage" 14168a6b5ecSdanielk1977 } 14268a6b5ecSdanielk1977 expr {[execsql {PRAGMA cache_size}] >= $nPage} 14368a6b5ecSdanielk1977} {1} 144ded6f4b2Sdanielk1977do_test exclusive2-1.2 { 145ded6f4b2Sdanielk1977 set ::sig [t1sig] 146f6f426beSaswift readPagerChangeCounter test.db 147ded6f4b2Sdanielk1977} {1} 148ded6f4b2Sdanielk1977do_test exclusive2-1.3 { 149ded6f4b2Sdanielk1977 t1sig 150ded6f4b2Sdanielk1977} $::sig 151ded6f4b2Sdanielk1977do_test exclusive2-1.4 { 152ded6f4b2Sdanielk1977 sqlite3 db2 test.db 153ded6f4b2Sdanielk1977 t1sig db2 154ded6f4b2Sdanielk1977} $::sig 155ded6f4b2Sdanielk1977do_test exclusive2-1.5 { 156ded6f4b2Sdanielk1977 execsql { 157*d47e1ccbSdan UPDATE t1 SET b=a, a=0; 158ded6f4b2Sdanielk1977 } db2 159ded6f4b2Sdanielk1977 expr {[t1sig db2] eq $::sig} 160ded6f4b2Sdanielk1977} 0 161ded6f4b2Sdanielk1977do_test exclusive2-1.6 { 162f6f426beSaswift readPagerChangeCounter test.db 163ded6f4b2Sdanielk1977} {2} 164ded6f4b2Sdanielk1977do_test exclusive2-1.7 { 165ded6f4b2Sdanielk1977 pagerChangeCounter test.db 1 166ded6f4b2Sdanielk1977} {1} 167fa2a4772Sdanif {[permutation] != "memsubsys1"} { 168ded6f4b2Sdanielk1977 do_test exclusive2-1.9 { 169ded6f4b2Sdanielk1977 t1sig 170ded6f4b2Sdanielk1977 expr {[t1sig] eq $::sig} 171ded6f4b2Sdanielk1977 } {1} 172fa2a4772Sdan} 173ded6f4b2Sdanielk1977do_test exclusive2-1.10 { 174ded6f4b2Sdanielk1977 pagerChangeCounter test.db 2 175ded6f4b2Sdanielk1977} {2} 176ded6f4b2Sdanielk1977do_test exclusive2-1.11 { 177ded6f4b2Sdanielk1977 expr {[t1sig] eq $::sig} 178ded6f4b2Sdanielk1977} {0} 1797cfbeb7fSdandb2 close 180ded6f4b2Sdanielk1977 181ded6f4b2Sdanielk1977#-------------------------------------------------------------------- 182ded6f4b2Sdanielk1977# These tests - exclusive2-2.X - are similar to exclusive2-1.X, 183ded6f4b2Sdanielk1977# except that they are run with locking_mode=EXCLUSIVE. 184ded6f4b2Sdanielk1977# 185ded6f4b2Sdanielk1977# 1-3: Build a database with exclusive-access connection 1, 186ded6f4b2Sdanielk1977# calculate a signature. 187ded6f4b2Sdanielk1977# 4: Corrupt the database by writing 10000 bytes of garbage 188ded6f4b2Sdanielk1977# starting at the beginning of page 2. Check that connection 1 189ded6f4b2Sdanielk1977# still works. It should be accessing the in-memory cache. 190ded6f4b2Sdanielk1977# 5-6: Modify the dataase change-counter. Connection 1 still works 191ded6f4b2Sdanielk1977# entirely from in-memory cache, because it doesn't check the 192ded6f4b2Sdanielk1977# change-counter. 193ded6f4b2Sdanielk1977# 7-8 Set the locking-mode back to normal. After the db is unlocked, 194ded6f4b2Sdanielk1977# SQLite detects the modified change-counter and discards the 195ded6f4b2Sdanielk1977# in-memory cache. Then it finds the corruption caused in step 4.... 196ded6f4b2Sdanielk1977# 19768a6b5ecSdanielk1977# As above, this test is only applicable if the pager cache is 19868a6b5ecSdanielk1977# large enough to hold the entire database. With 1024 byte pages, 1993aefabafSdrh# this means 19 pages. We also need to disable the soft-heap-limit 2003aefabafSdrh# to prevent memory-induced cache spills. 20168a6b5ecSdanielk1977# 202ded6f4b2Sdanielk1977do_test exclusive2-2.1 { 203c5053fb9Sdrh execsql {PRAGMA cache_size=1000;} 204ded6f4b2Sdanielk1977 execsql {PRAGMA locking_mode = exclusive;} 205ded6f4b2Sdanielk1977 execsql { 206ded6f4b2Sdanielk1977 BEGIN; 20786a88114Sdrh DELETE FROM t1; 20886a88114Sdrh INSERT INTO t1(a) VALUES(randstr(10, 400)); 20986a88114Sdrh INSERT INTO t1(a) VALUES(randstr(10, 400)); 21086a88114Sdrh INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 21186a88114Sdrh INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 21286a88114Sdrh INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 21386a88114Sdrh INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 21486a88114Sdrh INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 215ded6f4b2Sdanielk1977 COMMIT; 216ded6f4b2Sdanielk1977 SELECT count(*) FROM t1; 217ded6f4b2Sdanielk1977 } 218ded6f4b2Sdanielk1977} {64} 21968a6b5ecSdanielk1977do_test exclusive2-2.2.1 { 22068a6b5ecSdanielk1977 # Make sure the pager cache is large enough to store the 22168a6b5ecSdanielk1977 # entire database. 22268a6b5ecSdanielk1977 set nPage [expr [file size test.db]/1024] 22368a6b5ecSdanielk1977 if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { 22468a6b5ecSdanielk1977 execsql "PRAGMA cache_size = $nPage" 22568a6b5ecSdanielk1977 } 22668a6b5ecSdanielk1977 expr {[execsql {PRAGMA cache_size}] >= $nPage} 22768a6b5ecSdanielk1977} {1} 228ded6f4b2Sdanielk1977do_test exclusive2-2.2 { 229ded6f4b2Sdanielk1977 set ::sig [t1sig] 230f6f426beSaswift readPagerChangeCounter test.db 231ded6f4b2Sdanielk1977} {3} 232ded6f4b2Sdanielk1977do_test exclusive2-2.3 { 233ded6f4b2Sdanielk1977 t1sig 234ded6f4b2Sdanielk1977} $::sig 235ded6f4b2Sdanielk1977 236ded6f4b2Sdanielk1977do_test exclusive2-2.4 { 237831045ddSaswift set ::fd [open test.db RDWR] 238831045ddSaswift fconfigure $::fd -translation binary 239831045ddSaswift seek $::fd 1024 240831045ddSaswift puts -nonewline $::fd [string repeat [binary format c 0] 10000] 241831045ddSaswift flush $::fd 242ded6f4b2Sdanielk1977 t1sig 243ded6f4b2Sdanielk1977} $::sig 244ded6f4b2Sdanielk1977 245ded6f4b2Sdanielk1977do_test exclusive2-2.5 { 246831045ddSaswift pagerChangeCounter test.db 5 $::fd 247ded6f4b2Sdanielk1977} {5} 248ded6f4b2Sdanielk1977do_test exclusive2-2.6 { 249ded6f4b2Sdanielk1977 t1sig 250ded6f4b2Sdanielk1977} $::sig 251ded6f4b2Sdanielk1977do_test exclusive2-2.7 { 252ded6f4b2Sdanielk1977 execsql {PRAGMA locking_mode = normal} 253ded6f4b2Sdanielk1977 t1sig 254ded6f4b2Sdanielk1977} $::sig 255ded6f4b2Sdanielk1977 256ded6f4b2Sdanielk1977do_test exclusive2-2.8 { 257ded6f4b2Sdanielk1977 set rc [catch {t1sig} msg] 258ded6f4b2Sdanielk1977 list $rc $msg 259ded6f4b2Sdanielk1977} {1 {database disk image is malformed}} 260ded6f4b2Sdanielk1977 261ded6f4b2Sdanielk1977#-------------------------------------------------------------------- 262ded6f4b2Sdanielk1977# These tests - exclusive2-3.X - verify that the pager change-counter 263ded6f4b2Sdanielk1977# is only incremented by the first change when in exclusive access 264ded6f4b2Sdanielk1977# mode. In normal mode, the change-counter is incremented once 265ded6f4b2Sdanielk1977# per write-transaction. 266ded6f4b2Sdanielk1977# 267ded6f4b2Sdanielk1977 268ded6f4b2Sdanielk1977db close 269831045ddSaswiftcatch {close $::fd} 270fda06befSmistachkinforcedelete test.db 271fda06befSmistachkinforcedelete test.db-journal 272ded6f4b2Sdanielk1977 273ded6f4b2Sdanielk1977do_test exclusive2-3.0 { 274ded6f4b2Sdanielk1977 sqlite3 db test.db 275ded6f4b2Sdanielk1977 execsql { 276ded6f4b2Sdanielk1977 BEGIN; 277ded6f4b2Sdanielk1977 CREATE TABLE t1(a UNIQUE); 2787cfbeb7fSdan INSERT INTO t1 VALUES(randstr(200, 200)); 2797cfbeb7fSdan INSERT INTO t1 VALUES(randstr(200, 200)); 280ded6f4b2Sdanielk1977 COMMIT; 281ded6f4b2Sdanielk1977 } 282f6f426beSaswift readPagerChangeCounter test.db 283ded6f4b2Sdanielk1977} {1} 284ded6f4b2Sdanielk1977do_test exclusive2-3.1 { 285ded6f4b2Sdanielk1977 execsql { 2867cfbeb7fSdan INSERT INTO t1 VALUES(randstr(200, 200)); 287ded6f4b2Sdanielk1977 } 288f6f426beSaswift readPagerChangeCounter test.db 289ded6f4b2Sdanielk1977} {2} 290ded6f4b2Sdanielk1977do_test exclusive2-3.2 { 291ded6f4b2Sdanielk1977 execsql { 2927cfbeb7fSdan INSERT INTO t1 VALUES(randstr(200, 200)); 293ded6f4b2Sdanielk1977 } 294f6f426beSaswift readPagerChangeCounter test.db 295ded6f4b2Sdanielk1977} {3} 296ded6f4b2Sdanielk1977do_test exclusive2-3.3 { 297ded6f4b2Sdanielk1977 execsql { 298ded6f4b2Sdanielk1977 PRAGMA locking_mode = exclusive; 2997cfbeb7fSdan INSERT INTO t1 VALUES(randstr(200, 200)); 300ded6f4b2Sdanielk1977 } 301f6f426beSaswift readPagerChangeCounter test.db 302ded6f4b2Sdanielk1977} {4} 303ded6f4b2Sdanielk1977do_test exclusive2-3.4 { 304ded6f4b2Sdanielk1977 execsql { 3057cfbeb7fSdan INSERT INTO t1 VALUES(randstr(200, 200)); 306ded6f4b2Sdanielk1977 } 307f6f426beSaswift readPagerChangeCounter test.db 308ded6f4b2Sdanielk1977} {4} 309ded6f4b2Sdanielk1977do_test exclusive2-3.5 { 310ded6f4b2Sdanielk1977 execsql { 311ded6f4b2Sdanielk1977 PRAGMA locking_mode = normal; 3127cfbeb7fSdan INSERT INTO t1 VALUES(randstr(200, 200)); 3137cfbeb7fSdan } 3147cfbeb7fSdan readPagerChangeCounter test.db 3157cfbeb7fSdan} {4} 3167cfbeb7fSdando_test exclusive2-3.6 { 3177cfbeb7fSdan execsql { 3187cfbeb7fSdan INSERT INTO t1 VALUES(randstr(200, 200)); 319ded6f4b2Sdanielk1977 } 320f6f426beSaswift readPagerChangeCounter test.db 321d40d7ec7Sdrh} {5} 322c1a60c51Sdansqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) 323ded6f4b2Sdanielk1977 324ded6f4b2Sdanielk1977finish_test 325