3 # The author disclaims copyright to this source code. In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
12 # This file contains tests for using WAL databases in read-only mode.
15 set testdir [file dirname $argv0]
16 source $testdir/tester.tcl
17 source $testdir/lock_common.tcl
18 source $testdir/wal_common.tcl
19 set ::testprefix walro2
21 # And only if the build is WAL-capable.
28 proc copy_to_test2 {bZeroShm} {
29 forcecopy test.db test.db2
30 forcecopy test.db-wal test.db2-wal
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"
38 forcecopy test.db-shm test.db2-shm
42 # Most systems allocate the *-shm file in 32KB trunks. But on UNIX systems
43 # for which the getpagesize() call returns greater than 32K, the *-shm
44 # file is allocated in page-sized units (since you cannot mmap part of
45 # a page). The following code sets variable $MINSHMSZ to the smallest
46 # possible *-shm file (i.e. the greater of 32KB and the system page-size).
49 PRAGMA journal_mode = wal;
52 set MINSHMSZ [file size test.db-shm]
53 set dfltpgsz [db one {PRAGMA page_size}]
55 foreach bZeroShm {0 1} {
56 for {set pgsz 512} {$pgsz<=65536} {set pgsz [expr {$pgsz*2}]} {
57 set TN [expr $bZeroShm+1]-$pgsz
58 do_multiclient_test tn {
60 # Close all connections and delete the database.
67 # Do not run tests with the connections in the same process.
71 foreach c {code1 code2 code3} {
79 code2 { sqlite3 db2 test.db }
80 sql2 "PRAGMA page_size=$::pgsz"
82 CREATE TABLE t1(x, y);
83 PRAGMA journal_mode = WAL;
84 INSERT INTO t1 VALUES('a', 'b');
85 INSERT INTO t1 VALUES('c', 'd');
87 file exists test.db-shm
91 copy_to_test2 $bZeroShm
93 sqlite3 db file:test.db2?readonly_shm=1
96 sql1 { SELECT * FROM t1 }
99 sql1 { SELECT * FROM t1 }
103 code3 { sqlite3 db3 test.db2 }
104 sql3 { SELECT * FROM t1 }
108 sql1 { SELECT * FROM t1 }
116 code2 { sqlite3 db2 test.db }
117 sql2 "PRAGMA page_size=$::pgsz;"
119 INSERT INTO t1 VALUES('e', 'f');
120 INSERT INTO t1 VALUES('g', 'h');
122 file exists test.db-shm
126 copy_to_test2 $bZeroShm
128 sqlite3 db file:test.db2?readonly_shm=1
137 code3 { sqlite3 db3 test.db2 }
138 sql3 { SELECT * FROM t1 }
141 sql3 { INSERT INTO t1 VALUES('i', 'j') }
146 sql1 { SELECT * FROM t1 }
147 } {a b c d e f g h i j}
150 #-----------------------------------------------------------------------
151 # 3.1.*: That a readonly_shm connection can read a database file if both
152 # the *-wal and *-shm files are zero bytes in size.
154 # 3.2.*: That it flushes the cache if, between transactions on a db with a
155 # zero byte *-wal file, some other connection modifies the db, then
156 # does "PRAGMA wal_checkpoint=truncate" to truncate the wal file
157 # back to zero bytes in size.
159 # 3.3.*: That, if between transactions some other process wraps the wal
160 # file, the readonly_shm client reruns recovery.
162 catch { code1 { db close } }
163 catch { code2 { db2 close } }
164 catch { code3 { db3 close } }
166 list [file exists test.db-wal] [file exists test.db-shm]
169 close [open test.db-wal w]
170 close [open test.db-shm w]
172 sqlite3 db file:test.db?readonly_shm=1
174 sql1 { SELECT * FROM t1 }
178 list [file size test.db-wal] [file size test.db-shm]
181 code2 { sqlite3 db2 test.db }
182 sql2 { INSERT INTO t1 VALUES(1, 2) ; PRAGMA wal_checkpoint=truncate }
184 sql1 { SELECT * FROM t1 }
185 } {a b c d e f g h 1 2}
186 if {$pgsz!=$dfltpgsz} continue
188 list [file size test.db-wal] [file size test.db-shm]
191 code2 { sqlite3 db2 test.db }
193 INSERT INTO t1 VALUES(3, 4);
194 INSERT INTO t1 VALUES(5, 6);
195 INSERT INTO t1 VALUES(7, 8);
196 INSERT INTO t1 VALUES(9, 10);
200 list [file size test.db-wal] [file size test.db-shm]
201 } [list [wal_file_size 4 1024] $MINSHMSZ]
203 code1 { sqlite3 db file:test.db?readonly_shm=1 }
204 sql1 { SELECT * FROM t1 }
205 } {a b c d e f g h 1 2 3 4 5 6 7 8 9 10}
207 code2 { sqlite3 db2 test.db }
209 PRAGMA wal_checkpoint;
211 INSERT INTO t1 VALUES('i', 'ii');
214 list [file size test.db-wal] [file size test.db-shm]
215 } [list [wal_file_size 4 1024] $MINSHMSZ]
217 sql1 { SELECT * FROM t1 }
220 #-----------------------------------------------------------------------
223 catch { code1 { db close } }
224 catch { code2 { db2 close } }
225 catch { code3 { db3 close } }
228 code1 { forcedelete test.db }
229 code1 { sqlite3 db test.db }
231 PRAGMA journal_mode = wal;
233 INSERT INTO t1 VALUES('hello');
234 INSERT INTO t1 VALUES('world');
237 copy_to_test2 $bZeroShm
243 code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
244 sql2 { SELECT * FROM t1 }
248 code3 { sqlite3 db3 test.db2 }
250 INSERT INTO t1 VALUES('!');
251 PRAGMA wal_checkpoint = truncate;
256 sql2 { SELECT * FROM t1 }
259 catch { code1 { db close } }
260 catch { code2 { db2 close } }
261 catch { code3 { db3 close } }
264 code1 { sqlite3 db test.db }
266 INSERT INTO t1 VALUES('!');
267 INSERT INTO t1 VALUES('!');
269 PRAGMA cache_size = 10;
274 SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500
276 INSERT INTO t2 SELECT randomblob(500) FROM s;
277 SELECT count(*) FROM t2;
280 set sz [file size test.db-wal]
281 do_test $TN.4.2.2.(sz=$sz) {
285 file_control_persist_wal db 1; db close
287 copy_to_test2 $bZeroShm
288 code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
291 SELECT count(*) FROM t2;
293 } {hello world ! ! 0}
295 #-----------------------------------------------------------------------
298 catch { code1 { db close } }
299 catch { code2 { db2 close } }
300 catch { code3 { db3 close } }
303 code1 { forcedelete test.db }
304 code1 { sqlite3 db test.db }
306 PRAGMA journal_mode = wal;
308 INSERT INTO t1 VALUES('hello');
309 INSERT INTO t1 VALUES('world');
310 INSERT INTO t1 VALUES('!');
311 INSERT INTO t1 VALUES('world');
312 INSERT INTO t1 VALUES('hello');
315 copy_to_test2 $bZeroShm
321 code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
325 } {hello world ! world hello}
329 proc handle_read {op args} {
330 if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
331 set ::res2 [sql2 { SELECT * FROM t1 }]
333 puts "$msg xRead $args"
336 testvfs tvfs -fullshm 1
338 sqlite3 db file:test.db2?vfs=tvfs
339 db eval { SELECT * FROM sqlite_master }
342 tvfs script handle_read
345 PRAGMA wal_checkpoint = truncate;
348 } {hello world ! world hello}
352 code1 { tvfs delete }
355 #-----------------------------------------------------------------------
358 catch { code1 { db close } }
359 catch { code2 { db2 close } }
360 catch { code3 { db3 close } }
363 code1 { forcedelete test.db }
364 code1 { sqlite3 db test.db }
366 PRAGMA journal_mode = wal;
368 INSERT INTO t1 VALUES('hello');
369 INSERT INTO t1 VALUES('world');
370 INSERT INTO t1 VALUES('!');
371 INSERT INTO t1 VALUES('world');
372 INSERT INTO t1 VALUES('hello');
375 copy_to_test2 $bZeroShm
383 proc handle_read {op args} {
384 if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
387 code2 { sqlite3 db2 test.db2 }
388 sql2 { PRAGMA wal_checkpoint = truncate }
393 testvfs tvfs -fullshm 1
396 tvfs script handle_read
398 sqlite3 db file:test.db2?readonly_shm=1&vfs=tvfs
399 db eval { SELECT * FROM t1 }
401 } {hello world ! world hello}
405 code1 { tvfs delete }
409 } ;# foreach bZeroShm