Snapshot of upstream SQLite 3.34.1
[sqlcipher.git] / test / walvfs.test
blobf21b65e7e9f3fc092dac7fcbe9da8c77fd36d33c
1 # 2018 December 23
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 #***********************************************************************
11 # This file implements regression tests for SQLite library.  The
12 # focus of this file is testing the operation of the library in
13 # "PRAGMA journal_mode=WAL" mode.
16 set testdir [file dirname $argv0]
17 source $testdir/tester.tcl
18 source $testdir/lock_common.tcl
19 source $testdir/malloc_common.tcl
20 source $testdir/wal_common.tcl
21 set testprefix walvfs
23 ifcapable !wal {finish_test ; return }
25 db close
26 testvfs tvfs 
27 tvfs script xSync
28 tvfs filter xSync
29 set ::sync_count 0
30 proc xSync {method file args} {
31   if {[file tail $file]=="test.db-wal"} {
32     incr ::sync_count
33   }
37 #-------------------------------------------------------------------------
38 # Test that if IOCAP_SEQUENTIAL is set, the wal-header is not synced to
39 # disk immediately after it is written.
41 sqlite3 db test.db -vfs tvfs
42 do_execsql_test 1.0 {
43   PRAGMA auto_vacuum = 0;
44   PRAGMA journal_mode = wal;
45   PRAGMA synchronous = normal;
46   CREATE TABLE t1(a, b, c);
47   INSERT INTO t1 VALUES(1, 2, 3);
48   INSERT INTO t1 VALUES(4, 5, 6);
49   INSERT INTO t1 VALUES(7, 8, 9);
50   PRAGMA wal_checkpoint;
51 } {wal 0 5 5}
53 set ::sync_count 0
54 do_test 1.1 {
55   execsql { INSERT INTO t1 VALUES(10, 11, 12) }
56   set ::sync_count
57 } 1
59 db close
60 tvfs devchar sequential
61 sqlite3 db test.db -vfs tvfs
62 do_execsql_test 1.2 {
63   PRAGMA synchronous = normal;
64   INSERT INTO t1 VALUES(13, 14, 15);
65   INSERT INTO t1 VALUES(16, 17, 18);
66   PRAGMA wal_checkpoint;
67 } {0 4 4}
69 set ::sync_count 0
70 do_test 1.3 {
71   execsql { INSERT INTO t1 VALUES(10, 11, 12) }
72   set ::sync_count
73 } 0
75 #-------------------------------------------------------------------------
76 # Test that "PRAGMA journal_size_limit" works in wal mode.
78 reset_db
79 do_execsql_test 2.0 {
80   PRAGMA journal_size_limit = 10000;
81   CREATE TABLE t1(x);
82   PRAGMA journal_mode = wal;
83   WITH s(i) AS (
84     SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
85   )
86   INSERT INTO t1 SELECT randomblob(750) FROM s;
87 } {10000 wal}
88 do_test 2.1 {
89   expr [file size test.db-wal]>12000
90 } {1}
91 do_test 2.2 {
92   execsql {
93     PRAGMA wal_checkpoint;
94     INSERT INTO t1 VALUES(randomblob(750));
95   }
96   file size test.db-wal
97 } {10000}
98 do_test 2.3 {
99   execsql {
100     PRAGMA journal_size_limit = 8000;
101     PRAGMA wal_checkpoint;
102     INSERT INTO t1 VALUES(randomblob(750));
103   }
104   file size test.db-wal
105 } {8000}
107 #-------------------------------------------------------------------------
108 # Test that a checkpoint may be interrupted using sqlite3_interrupt().
109 # And that the error code is SQLITE_NOMEM, not SQLITE_INTERRUPT, if
110 # an OOM error occurs just before the sqlite3_interrupt() call.
112 reset_db
113 db close
114 sqlite3 db test.db -vfs tvfs
115 tvfs filter {}
117 do_execsql_test 3.0 {
118   CREATE TABLE t1(x);
119   PRAGMA journal_mode = wal;
120   WITH s(i) AS (
121     SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
122   )
123   INSERT INTO t1 SELECT randomblob(750) FROM s;
124 } {wal}
126 tvfs filter xWrite
127 tvfs script xWrite
128 set ::cnt 2
129 proc xWrite {method file args} {
130   if {[file tail $file]=="test.db"} {
131     incr ::cnt -1
132     if {$::cnt==0} {
133       sqlite3_interrupt db
134     }
135   }
136   return SQLITE_OK
139 do_catchsql_test 3.1 {
140   PRAGMA wal_checkpoint
141 } {1 interrupted}
143 set ::cnt 2
144 proc xWrite {method file args} {
145   if {[file tail $file]=="test.db"} {
146     incr ::cnt -1
147     if {$::cnt==0} {
148       sqlite3_memdebug_fail 1 -repeat 0
149       catchsql { SELECT 'a big long string!' }
150       sqlite3_interrupt db
151     }
152   }
153   return SQLITE_OK
156 do_catchsql_test 3.2 {
157   PRAGMA wal_checkpoint
158 } {1 {out of memory}}
160 #-------------------------------------------------------------------------
162 reset_db
163 db close
164 do_test 4.0 {
165   sqlite3 db test.db -vfs tvfs
166   execsql {
167     CREATE TABLE t1(x);
168     PRAGMA journal_mode = wal;
169     WITH s(i) AS (
170         SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
171     )
172     INSERT INTO t1 SELECT randomblob(750) FROM s;
173   } db
174 } {wal}
175 db close
177 tvfs filter xShmMap
178 tvfs script xShmMap
179 proc xShmMap {method file args} { 
180   return SQLITE_READONLY 
182 sqlite3 db test.db -vfs tvfs
183 do_catchsql_test 4.1 {
184   SELECT count(*) FROM t1
185 } {1 {attempt to write a readonly database}}
187 set ::cnt 5
188 tvfs filter {xShmMap xShmLock}
189 proc xShmMap {method file name args} { 
190   switch -- $method {
191     xShmMap {  return SQLITE_READONLY }
192     xShmLock {
193       if {$args == "{0 1 lock shared}"} {
194         incr ::cnt -1
195         if {$::cnt>0} { return SQLITE_BUSY }
196       }
197     }
198   }
199   return SQLITE_OK
201 do_catchsql_test 4.2 {
202   SELECT count(*) FROM t1
203 } {1 {attempt to write a readonly database}}
205 #-------------------------------------------------------------------------
207 reset_db
208 db close 
209 sqlite3 db test.db -vfs tvfs
210 tvfs filter {}
211 do_execsql_test 5.0 {
212   PRAGMA auto_vacuum = 0;
213   PRAGMA page_size = 1024;
214   CREATE TABLE t1(x);
215   PRAGMA journal_mode = wal;
216   WITH s(i) AS (
217       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
218   )
219   INSERT INTO t1 SELECT randomblob(750) FROM s;
220 } {wal}
222 do_execsql_test 5.1 {
223   SELECT count(*) FROM t1
224 } {20}
226 do_test 5.2 {
227   vfs_set_readmark db main 1 100
228   vfs_set_readmark db main 2 100
229   vfs_set_readmark db main 3 100
230   vfs_set_readmark db main 4 100
231 } {100}
233 do_execsql_test 5.3 {
234   SELECT count(*) FROM t1
235 } {20}
237 do_test 5.3 {
238   list [vfs_set_readmark db main 1] \
239        [vfs_set_readmark db main 2] \
240        [vfs_set_readmark db main 3] \
241        [vfs_set_readmark db main 4] 
242 } {24 100 100 100}
244 tvfs script xShmLock
245 tvfs filter xShmLock
246 set ::cnt 20
247 proc xShmLock {args} {
248   incr ::cnt -1
249   if {$::cnt>0} { return SQLITE_BUSY }
250   return SQLITE_OK
253 do_test 5.4 {
254   vfs_set_readmark db main 1 100
255   execsql { SELECT count(*) FROM t1 }
256 } {20}
258 vfs_set_readmark db main 1 100
259 vfs_set_readmark db main 2 100
260 vfs_set_readmark db main 3 100
261 vfs_set_readmark db main 4 100
263 tvfs script xShmMapLock
264 tvfs filter {xShmLock xShmMap}
265 proc xShmMapLock {method args} {
266   if {$method=="xShmMap"} {
267     return "SQLITE_READONLY"
268   }
269   return SQLITE_BUSY
272 sqlite3 db2 test.db -vfs tvfs
273 breakpoint
274 do_test 5.5 {
275   list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
276 } {1 {attempt to write a readonly database}}
278 tvfs filter {}
279 vfs_set_readmark db main 1 1
281 do_test 5.6 {
282   list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
283 } {0 20}
284 db2 close
285 db close
287 #-------------------------------------------------------------------------
288 # Cause an SQLITE_PROTOCOL while attempting to restart the wal file.
290 reset_db
291 tvfs filter {}
292 db close
293 sqlite3 db test.db -vfs tvfs
294 do_execsql_test 6.0 {
295   PRAGMA auto_vacuum = 0;
296   PRAGMA page_size = 1024;
297   CREATE TABLE t1(x);
298   PRAGMA journal_mode = wal;
299   WITH s(i) AS (
300       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
301   )
302   INSERT INTO t1 SELECT randomblob(750) FROM s;
303 } {wal}
305 do_test 6.1 {
306   execsql { PRAGMA wal_checkpoint } 
307   set {} {}
308 } {}
310 tvfs filter xShmLock
311 tvfs script xShmLock
312 set ::flag 0
313 proc xShmLock {method file handle spec} {
314   if {$::flag && [lrange $spec 2 end]=="lock shared"} {
315     return SQLITE_BUSY
316   }
317   if {$spec=="3 1 unlock shared"} {
318     set ::flag 1
319   }
320   return SQLITE_OK
323 puts "# WARNING: This next test takes around 12 seconds"
324 do_catchsql_test 6.2 {
325   INSERT INTO t1 VALUES(1);
326 } {1 {locking protocol}}
328 #-------------------------------------------------------------------------
329 # Check that a checkpoint fails if it cannot get the CHECKPOINTER lock
331 reset_db
332 tvfs filter {}
333 db close
334 sqlite3 db test.db -vfs tvfs
335 do_execsql_test 7.0 {
336   PRAGMA auto_vacuum = 0;
337   PRAGMA page_size = 1024;
338   CREATE TABLE t1(x);
339   PRAGMA journal_mode = wal;
340   WITH s(i) AS (
341       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
342   )
343   INSERT INTO t1 SELECT randomblob(750) FROM s;
344 } {wal}
346 tvfs script xShmLock
347 tvfs filter xShmLock
348 proc xShmLock {method file handle spec} {
349   if {$spec=="1 1 lock exclusive"} {
350     return SQLITE_BUSY
351   }
352   return SQLITE_OK
355 do_execsql_test 7.1 {
356   PRAGMA wal_checkpoint
357 } {1 -1 -1}
359 #-------------------------------------------------------------------------
360 # Check that the page cache is correctly flushed if a checkpointer using
361 # a version 2 VFS makes a checkpoint with an out-of-date cache.
363 reset_db
364 testvfs tvfs2 -iversion 2
365 db close
366 sqlite3 db test.db -vfs tvfs2
367 do_execsql_test 8.0 {
368   PRAGMA auto_vacuum = 0;
369   PRAGMA page_size = 1024;
370   CREATE TABLE t1(x);
371   PRAGMA journal_mode = wal;
372   WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
373   INSERT INTO t1 SELECT randomblob(75) FROM s;
374 } {wal}
376 do_execsql_test 8.1 { SELECT count(*) FROM t1 } {20}
378 do_test 8.2 {
379   sqlite3 db2 test.db -vfs tvfs2
380   execsql {
381     INSERT INTO t1 VALUES(randomblob(75));
382   } db2
383   db2 close
384 } {}
386 do_execsql_test 8.3 { 
387   PRAGMA wal_checkpoint;
388   SELECT count(*) FROM t1 
389 } {0 5 5 21}
390 db close
391 tvfs2 delete
393 #-------------------------------------------------------------------------
394 reset_db
395 db close
396 sqlite3 db test.db -vfs tvfs
397 do_execsql_test 9.0 {
398   PRAGMA auto_vacuum = 0;
399   PRAGMA page_size = 1024;
400   CREATE TABLE t1(x);
401   PRAGMA journal_mode = wal;
402   WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
403   INSERT INTO t1 SELECT randomblob(75) FROM s;
404 } {wal}
406 sqlite3 db2 test.db -vfs tvfs
407 tvfs filter {xShmMap xShmLock}
408 tvfs script xShmMap
409 proc xShmMap {method file handle args} {
410   switch -- $method {
411     xShmMap {
412       return "SQLITE_READONLY_CANTINIT"
413     }
414     xShmLock {
415       if {$args=="{3 1 lock shared}"} {
416         return "SQLITE_IOERR"
417       }
418     }
419   }
422 do_test 9.1 {
423   catchsql { SELECT count(*) FROM t1 } db2
424 } {1 {disk I/O error}}
426 db close
427 db2 close
428 tvfs delete
429 finish_test