1# 2007 March 24 2# 3# The author disclaims copyright to this source code. In place of 4# a legal notice, here is a blessing: 5# 6# May you do good and not evil. 7# May you find forgiveness for yourself and forgive others. 8# May you share freely, never taking more than you give. 9# 10#*********************************************************************** 11# This file implements regression tests for SQLite library. 12# 13# $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $ 14 15set testdir [file dirname $argv0] 16source $testdir/tester.tcl 17 18ifcapable {!pager_pragmas} { 19 finish_test 20 return 21} 22 23# This module does not work right if the cache spills at unexpected 24# moments. So disable the soft-heap-limit. 25# 26sqlite3_soft_heap_limit 0 27 28proc pagerChangeCounter {filename new {fd ""}} { 29 if {$fd==""} { 30 set fd [open $filename RDWR] 31 fconfigure $fd -translation binary -encoding binary 32 set needClose 1 33 } else { 34 set needClose 0 35 } 36 if {$new ne ""} { 37 seek $fd 24 38 set a [expr {($new&0xFF000000)>>24}] 39 set b [expr {($new&0x00FF0000)>>16}] 40 set c [expr {($new&0x0000FF00)>>8}] 41 set d [expr {($new&0x000000FF)}] 42 puts -nonewline $fd [binary format cccc $a $b $c $d] 43 flush $fd 44 } 45 46 seek $fd 24 47 foreach {a b c d} [list 0 0 0 0] {} 48 binary scan [read $fd 4] cccc a b c d 49 set ret [expr ($a&0x000000FF)<<24] 50 incr ret [expr ($b&0x000000FF)<<16] 51 incr ret [expr ($c&0x000000FF)<<8] 52 incr ret [expr ($d&0x000000FF)<<0] 53 54 if {$needClose} {close $fd} 55 return $ret 56} 57 58proc readPagerChangeCounter {filename} { 59 set fd [open $filename RDONLY] 60 fconfigure $fd -translation binary -encoding binary 61 62 seek $fd 24 63 foreach {a b c d} [list 0 0 0 0] {} 64 binary scan [read $fd 4] cccc a b c d 65 set ret [expr ($a&0x000000FF)<<24] 66 incr ret [expr ($b&0x000000FF)<<16] 67 incr ret [expr ($c&0x000000FF)<<8] 68 incr ret [expr ($d&0x000000FF)<<0] 69 70 close $fd 71 return $ret 72} 73 74 75proc t1sig {{db db}} { 76 execsql {SELECT count(*), md5sum(a) FROM t1} $db 77} 78do_test exclusive2-1.0 { 79 readPagerChangeCounter test.db 80} {0} 81 82#----------------------------------------------------------------------- 83# The following tests - exclusive2-1.X - check that: 84# 85# 1-3: Build a database with connection 1, calculate a signature. 86# 4-7: Modify the database using a second connection in a way that 87# does not modify the freelist, then reset the pager change-counter 88# to the value it had before the modifications. 89# 8: Check that using the first connection, the database signature 90# is still the same. This is because it uses the in-memory cache. 91# It can't tell the db has changed because we reset the change-counter. 92# 9: Increment the change-counter. 93# 10: Ensure that the first connection now sees the updated database. It 94# sees the change-counter has been incremented and discards the 95# invalid in-memory cache. 96# 97# This will only work if the database cache is large enough to hold 98# the entire database. In the case of 1024 byte pages, this means 99# the cache size must be at least 17. Otherwise, some pages will be 100# loaded from the database file in step 8. 101# 102# For similar reasons, this test does not work with the memsubsys1 permutation. 103# Permutation memsubsys1 configures the pcache subsystem to use a static 104# allocation of 24 pages (shared between all pagers). This is not enough for 105# this test. 106# 107do_test exclusive2-1.1 { 108 execsql { 109 BEGIN; 110 CREATE TABLE t1(a, b); 111 INSERT INTO t1(a) VALUES(randstr(10, 400)); 112 INSERT INTO t1(a) VALUES(randstr(10, 400)); 113 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 114 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 115 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 116 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 117 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 118 COMMIT; 119 SELECT count(*) FROM t1; 120 } 121} {64} 122do_test exclusive2-1.2.1 { 123 # Make sure the pager cache is large enough to store the 124 # entire database. 125 set nPage [expr [file size test.db]/1024] 126 if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { 127 execsql "PRAGMA cache_size = $nPage" 128 } 129 expr {[execsql {PRAGMA cache_size}] >= $nPage} 130} {1} 131do_test exclusive2-1.2 { 132 set ::sig [t1sig] 133 readPagerChangeCounter test.db 134} {1} 135do_test exclusive2-1.3 { 136 t1sig 137} $::sig 138do_test exclusive2-1.4 { 139 sqlite3 db2 test.db 140 t1sig db2 141} $::sig 142do_test exclusive2-1.5 { 143 execsql { 144 UPDATE t1 SET b=a, a=NULL; 145 } db2 146 expr {[t1sig db2] eq $::sig} 147} 0 148do_test exclusive2-1.6 { 149 readPagerChangeCounter test.db 150} {2} 151do_test exclusive2-1.7 { 152 pagerChangeCounter test.db 1 153} {1} 154if {[permutation] != "memsubsys1"} { 155 do_test exclusive2-1.9 { 156 t1sig 157 expr {[t1sig] eq $::sig} 158 } {1} 159} 160do_test exclusive2-1.10 { 161 pagerChangeCounter test.db 2 162} {2} 163do_test exclusive2-1.11 { 164 expr {[t1sig] eq $::sig} 165} {0} 166 167#-------------------------------------------------------------------- 168# These tests - exclusive2-2.X - are similar to exclusive2-1.X, 169# except that they are run with locking_mode=EXCLUSIVE. 170# 171# 1-3: Build a database with exclusive-access connection 1, 172# calculate a signature. 173# 4: Corrupt the database by writing 10000 bytes of garbage 174# starting at the beginning of page 2. Check that connection 1 175# still works. It should be accessing the in-memory cache. 176# 5-6: Modify the dataase change-counter. Connection 1 still works 177# entirely from in-memory cache, because it doesn't check the 178# change-counter. 179# 7-8 Set the locking-mode back to normal. After the db is unlocked, 180# SQLite detects the modified change-counter and discards the 181# in-memory cache. Then it finds the corruption caused in step 4.... 182# 183# As above, this test is only applicable if the pager cache is 184# large enough to hold the entire database. With 1024 byte pages, 185# this means 19 pages. We also need to disable the soft-heap-limit 186# to prevent memory-induced cache spills. 187# 188do_test exclusive2-2.1 { 189 execsql {PRAGMA cache_size=1000;} 190 execsql {PRAGMA locking_mode = exclusive;} 191 execsql { 192 BEGIN; 193 DELETE FROM t1; 194 INSERT INTO t1(a) VALUES(randstr(10, 400)); 195 INSERT INTO t1(a) VALUES(randstr(10, 400)); 196 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 197 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 198 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 199 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 200 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; 201 COMMIT; 202 SELECT count(*) FROM t1; 203 } 204} {64} 205do_test exclusive2-2.2.1 { 206 # Make sure the pager cache is large enough to store the 207 # entire database. 208 set nPage [expr [file size test.db]/1024] 209 if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { 210 execsql "PRAGMA cache_size = $nPage" 211 } 212 expr {[execsql {PRAGMA cache_size}] >= $nPage} 213} {1} 214do_test exclusive2-2.2 { 215 set ::sig [t1sig] 216 readPagerChangeCounter test.db 217} {3} 218do_test exclusive2-2.3 { 219 t1sig 220} $::sig 221 222do_test exclusive2-2.4 { 223 set ::fd [open test.db RDWR] 224 fconfigure $::fd -translation binary 225 seek $::fd 1024 226 puts -nonewline $::fd [string repeat [binary format c 0] 10000] 227 flush $::fd 228 t1sig 229} $::sig 230 231do_test exclusive2-2.5 { 232 pagerChangeCounter test.db 5 $::fd 233} {5} 234do_test exclusive2-2.6 { 235 t1sig 236} $::sig 237do_test exclusive2-2.7 { 238 execsql {PRAGMA locking_mode = normal} 239 t1sig 240} $::sig 241 242do_test exclusive2-2.8 { 243 set rc [catch {t1sig} msg] 244 list $rc $msg 245} {1 {database disk image is malformed}} 246 247#-------------------------------------------------------------------- 248# These tests - exclusive2-3.X - verify that the pager change-counter 249# is only incremented by the first change when in exclusive access 250# mode. In normal mode, the change-counter is incremented once 251# per write-transaction. 252# 253 254db close 255db2 close 256catch {close $::fd} 257file delete -force test.db 258file delete -force test.db-journal 259 260do_test exclusive2-3.0 { 261 sqlite3 db test.db 262 execsql { 263 BEGIN; 264 CREATE TABLE t1(a UNIQUE); 265 INSERT INTO t1 VALUES(randstr(10, 400)); 266 INSERT INTO t1 VALUES(randstr(10, 400)); 267 COMMIT; 268 } 269 readPagerChangeCounter test.db 270} {1} 271do_test exclusive2-3.1 { 272 execsql { 273 INSERT INTO t1 VALUES(randstr(10, 400)); 274 } 275 readPagerChangeCounter test.db 276} {2} 277do_test exclusive2-3.2 { 278 execsql { 279 INSERT INTO t1 VALUES(randstr(10, 400)); 280 } 281 readPagerChangeCounter test.db 282} {3} 283do_test exclusive2-3.3 { 284 execsql { 285 PRAGMA locking_mode = exclusive; 286 INSERT INTO t1 VALUES(randstr(10, 400)); 287 } 288 readPagerChangeCounter test.db 289} {4} 290do_test exclusive2-3.4 { 291 execsql { 292 INSERT INTO t1 VALUES(randstr(10, 400)); 293 } 294 readPagerChangeCounter test.db 295} {4} 296do_test exclusive2-3.5 { 297 execsql { 298 PRAGMA locking_mode = normal; 299 INSERT INTO t1 VALUES(randstr(10, 400)); 300 } 301 readPagerChangeCounter test.db 302} {4} 303do_test exclusive2-3.6 { 304 execsql { 305 INSERT INTO t1 VALUES(randstr(10, 400)); 306 } 307 readPagerChangeCounter test.db 308} {5} 309sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) 310 311finish_test 312