1# 2011 May 09 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# 12# This file contains tests for using WAL databases in read-only mode. 13# 14 15set testdir [file dirname $argv0] 16source $testdir/tester.tcl 17source $testdir/lock_common.tcl 18source $testdir/wal_common.tcl 19set ::testprefix walro2 20 21# And only if the build is WAL-capable. 22# 23ifcapable !wal { 24 finish_test 25 return 26} 27 28proc copy_to_test2 {bZeroShm} { 29 forcecopy test.db test.db2 30 forcecopy test.db-wal test.db2-wal 31 if {$bZeroShm} { 32 forcedelete test.db2-shm 33 set fd [open test.db2-shm w] 34 seek $fd [expr [file size test.db-shm]-1] 35 puts -nonewline $fd "\0" 36 close $fd 37 } else { 38 forcecopy test.db-shm test.db2-shm 39 } 40} 41 42foreach bZeroShm {0 1} { 43set TN [expr $bZeroShm+1] 44do_multiclient_test tn { 45 46 # Close all connections and delete the database. 47 # 48 code1 { db close } 49 code2 { db2 close } 50 code3 { db3 close } 51 forcedelete test.db 52 53 # Do not run tests with the connections in the same process. 54 # 55 if {$tn==2} continue 56 57 foreach c {code1 code2 code3} { 58 $c { 59 sqlite3_shutdown 60 sqlite3_config_uri 1 61 } 62 } 63 64 do_test $TN.1.1 { 65 code2 { sqlite3 db2 test.db } 66 sql2 { 67 CREATE TABLE t1(x, y); 68 PRAGMA journal_mode = WAL; 69 INSERT INTO t1 VALUES('a', 'b'); 70 INSERT INTO t1 VALUES('c', 'd'); 71 } 72 file exists test.db-shm 73 } {1} 74 75 do_test $TN.1.2.1 { 76 copy_to_test2 $bZeroShm 77 code1 { 78 sqlite3 db file:test.db2?readonly_shm=1 79 } 80 81 sql1 { SELECT * FROM t1 } 82 } {a b c d} 83 do_test $TN.1.2.2 { 84 sql1 { SELECT * FROM t1 } 85 } {a b c d} 86 87 do_test $TN.1.3.1 { 88 code3 { sqlite3 db3 test.db2 } 89 sql3 { SELECT * FROM t1 } 90 } {a b c d} 91 92 do_test $TN.1.3.2 { 93 sql1 { SELECT * FROM t1 } 94 } {a b c d} 95 96 code1 { db close } 97 code2 { db2 close } 98 code3 { db3 close } 99 100 do_test $TN.2.1 { 101 code2 { sqlite3 db2 test.db } 102 sql2 { 103 INSERT INTO t1 VALUES('e', 'f'); 104 INSERT INTO t1 VALUES('g', 'h'); 105 } 106 file exists test.db-shm 107 } {1} 108 109 do_test $TN.2.2 { 110 copy_to_test2 $bZeroShm 111 code1 { 112 sqlite3 db file:test.db2?readonly_shm=1 113 } 114 sql1 { 115 BEGIN; 116 SELECT * FROM t1; 117 } 118 } {a b c d e f g h} 119 120 do_test $TN.2.3.1 { 121 code3 { sqlite3 db3 test.db2 } 122 sql3 { SELECT * FROM t1 } 123 } {a b c d e f g h} 124 do_test $TN.2.3.2 { 125 sql3 { INSERT INTO t1 VALUES('i', 'j') } 126 code3 { db3 close } 127 sql1 { COMMIT } 128 } {} 129 do_test $TN.2.3.3 { 130 sql1 { SELECT * FROM t1 } 131 } {a b c d e f g h i j} 132 133 134 #----------------------------------------------------------------------- 135 # 3.1.*: That a readonly_shm connection can read a database file if both 136 # the *-wal and *-shm files are zero bytes in size. 137 # 138 # 3.2.*: That it flushes the cache if, between transactions on a db with a 139 # zero byte *-wal file, some other connection modifies the db, then 140 # does "PRAGMA wal_checkpoint=truncate" to truncate the wal file 141 # back to zero bytes in size. 142 # 143 # 3.3.*: That, if between transactions some other process wraps the wal 144 # file, the readonly_shm client reruns recovery. 145 # 146 catch { code1 { db close } } 147 catch { code2 { db2 close } } 148 catch { code3 { db3 close } } 149 do_test $TN.3.1.0 { 150 list [file exists test.db-wal] [file exists test.db-shm] 151 } {0 0} 152 do_test $TN.3.1.1 { 153 close [open test.db-wal w] 154 close [open test.db-shm w] 155 code1 { 156 sqlite3 db file:test.db?readonly_shm=1 157 } 158 sql1 { SELECT * FROM t1 } 159 } {a b c d e f g h} 160 161 do_test $TN.3.2.0 { 162 list [file size test.db-wal] [file size test.db-shm] 163 } {0 0} 164 do_test $TN.3.2.1 { 165 code2 { sqlite3 db2 test.db } 166 sql2 { INSERT INTO t1 VALUES(1, 2) ; PRAGMA wal_checkpoint=truncate } 167 code2 { db2 close } 168 sql1 { SELECT * FROM t1 } 169 } {a b c d e f g h 1 2} 170 do_test $TN.3.2.2 { 171 list [file size test.db-wal] [file size test.db-shm] 172 } {0 32768} 173 174 do_test $TN.3.3.0 { 175 code2 { sqlite3 db2 test.db } 176 sql2 { 177 INSERT INTO t1 VALUES(3, 4); 178 INSERT INTO t1 VALUES(5, 6); 179 INSERT INTO t1 VALUES(7, 8); 180 INSERT INTO t1 VALUES(9, 10); 181 } 182 code2 { db2 close } 183 code1 { db close } 184 list [file size test.db-wal] [file size test.db-shm] 185 } [list [wal_file_size 4 1024] 32768] 186 do_test $TN.3.3.1 { 187 code1 { sqlite3 db file:test.db?readonly_shm=1 } 188 sql1 { SELECT * FROM t1 } 189 } {a b c d e f g h 1 2 3 4 5 6 7 8 9 10} 190 do_test $TN.3.3.2 { 191 code2 { sqlite3 db2 test.db } 192 sql2 { 193 PRAGMA wal_checkpoint; 194 DELETE FROM t1; 195 INSERT INTO t1 VALUES('i', 'ii'); 196 } 197 code2 { db2 close } 198 list [file size test.db-wal] [file size test.db-shm] 199 } [list [wal_file_size 4 1024] 32768] 200 do_test $TN.3.3.3 { 201 sql1 { SELECT * FROM t1 } 202 } {i ii} 203 204 #----------------------------------------------------------------------- 205 # 206 # 207 catch { code1 { db close } } 208 catch { code2 { db2 close } } 209 catch { code3 { db3 close } } 210 211 do_test $TN.4.0 { 212 code1 { forcedelete test.db } 213 code1 { sqlite3 db test.db } 214 sql1 { 215 PRAGMA journal_mode = wal; 216 CREATE TABLE t1(x); 217 INSERT INTO t1 VALUES('hello'); 218 INSERT INTO t1 VALUES('world'); 219 } 220 221 copy_to_test2 $bZeroShm 222 223 code1 { db close } 224 } {} 225 226 do_test $TN.4.1.1 { 227 code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } 228 sql2 { SELECT * FROM t1 } 229 } {hello world} 230 231 do_test $TN.4.1.2 { 232 code3 { sqlite3 db3 test.db2 } 233 sql3 { 234 INSERT INTO t1 VALUES('!'); 235 PRAGMA wal_checkpoint = truncate; 236 } 237 code3 { db3 close } 238 } {} 239 do_test $TN.4.1.3 { 240 sql2 { SELECT * FROM t1 } 241 } {hello world !} 242 243 catch { code1 { db close } } 244 catch { code2 { db2 close } } 245 catch { code3 { db3 close } } 246 247 do_test $TN.4.2.1 { 248 code1 { sqlite3 db test.db } 249 sql1 { 250 INSERT INTO t1 VALUES('!'); 251 INSERT INTO t1 VALUES('!'); 252 253 PRAGMA cache_size = 10; 254 CREATE TABLE t2(x); 255 256 BEGIN; 257 WITH s(i) AS ( 258 SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500 259 ) 260 INSERT INTO t2 SELECT randomblob(500) FROM s; 261 SELECT count(*) FROM t2; 262 } 263 } {500} 264 do_test $TN.4.2.2 { 265 file size test.db-wal 266 } {461152} 267 do_test $TN.4.2.4 { 268 file_control_persist_wal db 1; db close 269 270 copy_to_test2 $bZeroShm 271 code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } 272 sql2 { 273 SELECT * FROM t1; 274 SELECT count(*) FROM t2; 275 } 276 } {hello world ! ! 0} 277 278 #----------------------------------------------------------------------- 279 # 280 # 281 catch { code1 { db close } } 282 catch { code2 { db2 close } } 283 catch { code3 { db3 close } } 284 285 do_test $TN.5.0 { 286 code1 { forcedelete test.db } 287 code1 { sqlite3 db test.db } 288 sql1 { 289 PRAGMA journal_mode = wal; 290 CREATE TABLE t1(x); 291 INSERT INTO t1 VALUES('hello'); 292 INSERT INTO t1 VALUES('world'); 293 INSERT INTO t1 VALUES('!'); 294 INSERT INTO t1 VALUES('world'); 295 INSERT INTO t1 VALUES('hello'); 296 } 297 298 copy_to_test2 $bZeroShm 299 300 code1 { db close } 301 } {} 302 303 do_test $TN.5.1 { 304 code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } 305 sql2 { 306 SELECT * FROM t1; 307 } 308 } {hello world ! world hello} 309 310 do_test $TN.5.2 { 311 code1 { 312 proc handle_read {op args} { 313 if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} { 314 set ::res2 [sql2 { SELECT * FROM t1 }] 315 } 316 puts "$msg xRead $args" 317 return "SQLITE_OK" 318 } 319 testvfs tvfs -fullshm 1 320 321 sqlite3 db file:test.db2?vfs=tvfs 322 db eval { SELECT * FROM sqlite_master } 323 324 tvfs filter xRead 325 tvfs script handle_read 326 } 327 sql1 { 328 PRAGMA wal_checkpoint = truncate; 329 } 330 code1 { set ::res2 } 331 } {hello world ! world hello} 332 333 do_test $TN.5.3 { 334 code1 { db close } 335 code1 { tvfs delete } 336 } {} 337 338 #----------------------------------------------------------------------- 339 # 340 # 341 catch { code1 { db close } } 342 catch { code2 { db2 close } } 343 catch { code3 { db3 close } } 344 345 do_test $TN.6.1 { 346 code1 { forcedelete test.db } 347 code1 { sqlite3 db test.db } 348 sql1 { 349 PRAGMA journal_mode = wal; 350 CREATE TABLE t1(x); 351 INSERT INTO t1 VALUES('hello'); 352 INSERT INTO t1 VALUES('world'); 353 INSERT INTO t1 VALUES('!'); 354 INSERT INTO t1 VALUES('world'); 355 INSERT INTO t1 VALUES('hello'); 356 } 357 358 copy_to_test2 $bZeroShm 359 360 code1 { db close } 361 } {} 362 363 do_test $TN.6.2 { 364 code1 { 365 set ::nRem 5 366 proc handle_read {op args} { 367 if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} { 368 incr ::nRem -1 369 if {$::nRem==0} { 370 code2 { sqlite3 db2 test.db2 } 371 sql2 { PRAGMA wal_checkpoint = truncate } 372 } 373 } 374 return "SQLITE_OK" 375 } 376 testvfs tvfs -fullshm 1 377 378 tvfs filter xRead 379 tvfs script handle_read 380 381 sqlite3 db file:test.db2?readonly_shm=1&vfs=tvfs 382 db eval { SELECT * FROM t1 } 383 } 384 } {hello world ! world hello} 385 386 do_test $TN.6.3 { 387 code1 { db close } 388 code1 { tvfs delete } 389 } {} 390} 391} ;# foreach bZeroShm 392 393finish_test 394