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