| 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 ifcapable {!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 # | |
| 26 sqlite3_soft_heap_limit 0 | |
| 27 | |
| 28 proc 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 | |
| 58 proc 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 | |
| 75 proc t1sig {{db db}} { | |
| 76 execsql {SELECT count(*), md5sum(a) FROM t1} $db | |
| 77 } | |
| 78 do_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-9: 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 do_test exclusive2-1.1 { | |
| 103 execsql { | |
| 104 BEGIN; | |
| 105 CREATE TABLE t1(a, b); | |
| 106 INSERT INTO t1(a) VALUES(randstr(10, 400)); | |
| 107 INSERT INTO t1(a) VALUES(randstr(10, 400)); | |
| 108 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 109 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 110 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 111 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 112 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 113 COMMIT; | |
| 114 SELECT count(*) FROM t1; | |
| 115 } | |
| 116 } {64} | |
| 117 do_test exclusive2-1.2.1 { | |
| 118 # Make sure the pager cache is large enough to store the | |
| 119 # entire database. | |
| 120 set nPage [expr [file size test.db]/1024] | |
| 121 if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { | |
| 122 execsql "PRAGMA cache_size = $nPage" | |
| 123 } | |
| 124 expr {[execsql {PRAGMA cache_size}] >= $nPage} | |
| 125 } {1} | |
| 126 do_test exclusive2-1.2 { | |
| 127 set ::sig [t1sig] | |
| 128 readPagerChangeCounter test.db | |
| 129 } {1} | |
| 130 do_test exclusive2-1.3 { | |
| 131 t1sig | |
| 132 } $::sig | |
| 133 do_test exclusive2-1.4 { | |
| 134 sqlite3 db2 test.db | |
| 135 t1sig db2 | |
| 136 } $::sig | |
| 137 do_test exclusive2-1.5 { | |
| 138 execsql { | |
| 139 UPDATE t1 SET b=a, a=NULL; | |
| 140 } db2 | |
| 141 expr {[t1sig db2] eq $::sig} | |
| 142 } 0 | |
| 143 do_test exclusive2-1.6 { | |
| 144 readPagerChangeCounter test.db | |
| 145 } {2} | |
| 146 do_test exclusive2-1.7 { | |
| 147 pagerChangeCounter test.db 1 | |
| 148 } {1} | |
| 149 do_test exclusive2-1.9 { | |
| 150 t1sig | |
| 151 expr {[t1sig] eq $::sig} | |
| 152 } {1} | |
| 153 do_test exclusive2-1.10 { | |
| 154 pagerChangeCounter test.db 2 | |
| 155 } {2} | |
| 156 do_test exclusive2-1.11 { | |
| 157 expr {[t1sig] eq $::sig} | |
| 158 } {0} | |
| 159 | |
| 160 #-------------------------------------------------------------------- | |
| 161 # These tests - exclusive2-2.X - are similar to exclusive2-1.X, | |
| 162 # except that they are run with locking_mode=EXCLUSIVE. | |
| 163 # | |
| 164 # 1-3: Build a database with exclusive-access connection 1, | |
| 165 # calculate a signature. | |
| 166 # 4: Corrupt the database by writing 10000 bytes of garbage | |
| 167 # starting at the beginning of page 2. Check that connection 1 | |
| 168 # still works. It should be accessing the in-memory cache. | |
| 169 # 5-6: Modify the dataase change-counter. Connection 1 still works | |
| 170 # entirely from in-memory cache, because it doesn't check the | |
| 171 # change-counter. | |
| 172 # 7-8 Set the locking-mode back to normal. After the db is unlocked, | |
| 173 # SQLite detects the modified change-counter and discards the | |
| 174 # in-memory cache. Then it finds the corruption caused in step 4.... | |
| 175 # | |
| 176 # As above, this test is only applicable if the pager cache is | |
| 177 # large enough to hold the entire database. With 1024 byte pages, | |
| 178 # this means 19 pages. We also need to disable the soft-heap-limit | |
| 179 # to prevent memory-induced cache spills. | |
| 180 # | |
| 181 do_test exclusive2-2.1 { | |
| 182 execsql {PRAGMA cache_size=1000;} | |
| 183 execsql {PRAGMA locking_mode = exclusive;} | |
| 184 execsql { | |
| 185 BEGIN; | |
| 186 DELETE FROM t1; | |
| 187 INSERT INTO t1(a) VALUES(randstr(10, 400)); | |
| 188 INSERT INTO t1(a) VALUES(randstr(10, 400)); | |
| 189 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 190 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 191 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 192 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 193 INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; | |
| 194 COMMIT; | |
| 195 SELECT count(*) FROM t1; | |
| 196 } | |
| 197 } {64} | |
| 198 do_test exclusive2-2.2.1 { | |
| 199 # Make sure the pager cache is large enough to store the | |
| 200 # entire database. | |
| 201 set nPage [expr [file size test.db]/1024] | |
| 202 if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} { | |
| 203 execsql "PRAGMA cache_size = $nPage" | |
| 204 } | |
| 205 expr {[execsql {PRAGMA cache_size}] >= $nPage} | |
| 206 } {1} | |
| 207 do_test exclusive2-2.2 { | |
| 208 set ::sig [t1sig] | |
| 209 readPagerChangeCounter test.db | |
| 210 } {3} | |
| 211 do_test exclusive2-2.3 { | |
| 212 t1sig | |
| 213 } $::sig | |
| 214 | |
| 215 do_test exclusive2-2.4 { | |
| 216 set ::fd [open test.db RDWR] | |
| 217 fconfigure $::fd -translation binary | |
| 218 seek $::fd 1024 | |
| 219 puts -nonewline $::fd [string repeat [binary format c 0] 10000] | |
| 220 flush $::fd | |
| 221 t1sig | |
| 222 } $::sig | |
| 223 | |
| 224 do_test exclusive2-2.5 { | |
| 225 pagerChangeCounter test.db 5 $::fd | |
| 226 } {5} | |
| 227 do_test exclusive2-2.6 { | |
| 228 t1sig | |
| 229 } $::sig | |
| 230 do_test exclusive2-2.7 { | |
| 231 execsql {PRAGMA locking_mode = normal} | |
| 232 t1sig | |
| 233 } $::sig | |
| 234 | |
| 235 do_test exclusive2-2.8 { | |
| 236 set rc [catch {t1sig} msg] | |
| 237 list $rc $msg | |
| 238 } {1 {database disk image is malformed}} | |
| 239 | |
| 240 #-------------------------------------------------------------------- | |
| 241 # These tests - exclusive2-3.X - verify that the pager change-counter | |
| 242 # is only incremented by the first change when in exclusive access | |
| 243 # mode. In normal mode, the change-counter is incremented once | |
| 244 # per write-transaction. | |
| 245 # | |
| 246 | |
| 247 db close | |
| 248 db2 close | |
| 249 catch {close $::fd} | |
| 250 file delete -force test.db | |
| 251 file delete -force test.db-journal | |
| 252 | |
| 253 do_test exclusive2-3.0 { | |
| 254 sqlite3 db test.db | |
| 255 execsql { | |
| 256 BEGIN; | |
| 257 CREATE TABLE t1(a UNIQUE); | |
| 258 INSERT INTO t1 VALUES(randstr(10, 400)); | |
| 259 INSERT INTO t1 VALUES(randstr(10, 400)); | |
| 260 COMMIT; | |
| 261 } | |
| 262 readPagerChangeCounter test.db | |
| 263 } {1} | |
| 264 do_test exclusive2-3.1 { | |
| 265 execsql { | |
| 266 INSERT INTO t1 VALUES(randstr(10, 400)); | |
| 267 } | |
| 268 readPagerChangeCounter test.db | |
| 269 } {2} | |
| 270 do_test exclusive2-3.2 { | |
| 271 execsql { | |
| 272 INSERT INTO t1 VALUES(randstr(10, 400)); | |
| 273 } | |
| 274 readPagerChangeCounter test.db | |
| 275 } {3} | |
| 276 do_test exclusive2-3.3 { | |
| 277 execsql { | |
| 278 PRAGMA locking_mode = exclusive; | |
| 279 INSERT INTO t1 VALUES(randstr(10, 400)); | |
| 280 } | |
| 281 readPagerChangeCounter test.db | |
| 282 } {4} | |
| 283 do_test exclusive2-3.4 { | |
| 284 execsql { | |
| 285 INSERT INTO t1 VALUES(randstr(10, 400)); | |
| 286 } | |
| 287 readPagerChangeCounter test.db | |
| 288 } {4} | |
| 289 do_test exclusive2-3.5 { | |
| 290 execsql { | |
| 291 PRAGMA locking_mode = normal; | |
| 292 INSERT INTO t1 VALUES(randstr(10, 400)); | |
| 293 } | |
| 294 readPagerChangeCounter test.db | |
| 295 } {4} | |
| 296 do_test exclusive2-3.6 { | |
| 297 execsql { | |
| 298 INSERT INTO t1 VALUES(randstr(10, 400)); | |
| 299 } | |
| 300 readPagerChangeCounter test.db | |
| 301 } {5} | |
| 302 sqlite3_soft_heap_limit $soft_limit | |
| 303 | |
| 304 finish_test | |
| OLD | NEW |