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