Snapshot of upstream SQLite 3.45.3
[sqlcipher.git] / test / walvfs.test
blobd32cbd73f1b70bb672a640abe6c3fe0d2d64ae41
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.
15 # TESTRUNNER: slow
17 set testdir [file dirname $argv0]
18 source $testdir/tester.tcl
19 source $testdir/lock_common.tcl
20 source $testdir/malloc_common.tcl
21 source $testdir/wal_common.tcl
22 set testprefix walvfs
24 ifcapable !wal {finish_test ; return }
26 db close
27 testvfs tvfs 
28 tvfs script xSync
29 tvfs filter xSync
30 set ::sync_count 0
31 proc xSync {method file args} {
32   if {[file tail $file]=="test.db-wal"} {
33     incr ::sync_count
34   }
38 #-------------------------------------------------------------------------
39 # Test that if IOCAP_SEQUENTIAL is set, the wal-header is not synced to
40 # disk immediately after it is written.
42 sqlite3 db test.db -vfs tvfs
43 do_execsql_test 1.0 {
44   PRAGMA auto_vacuum = 0;
45   PRAGMA journal_mode = wal;
46   PRAGMA synchronous = normal;
47   CREATE TABLE t1(a, b, c);
48   INSERT INTO t1 VALUES(1, 2, 3);
49   INSERT INTO t1 VALUES(4, 5, 6);
50   INSERT INTO t1 VALUES(7, 8, 9);
51   PRAGMA wal_checkpoint;
52 } {wal 0 5 5}
54 set ::sync_count 0
55 do_test 1.1 {
56   execsql { INSERT INTO t1 VALUES(10, 11, 12) }
57   set ::sync_count
58 } 1
60 db close
61 tvfs devchar sequential
62 sqlite3 db test.db -vfs tvfs
63 do_execsql_test 1.2 {
64   PRAGMA synchronous = normal;
65   INSERT INTO t1 VALUES(13, 14, 15);
66   INSERT INTO t1 VALUES(16, 17, 18);
67   PRAGMA wal_checkpoint;
68 } {0 4 4}
70 set ::sync_count 0
71 do_test 1.3 {
72   execsql { INSERT INTO t1 VALUES(10, 11, 12) }
73   set ::sync_count
74 } 0
76 #-------------------------------------------------------------------------
77 # Test that "PRAGMA journal_size_limit" works in wal mode.
79 reset_db
80 do_execsql_test 2.0 {
81   PRAGMA journal_size_limit = 10000;
82   CREATE TABLE t1(x);
83   PRAGMA journal_mode = wal;
84   WITH s(i) AS (
85     SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
86   )
87   INSERT INTO t1 SELECT randomblob(750) FROM s;
88 } {10000 wal}
89 do_test 2.1 {
90   expr [file size test.db-wal]>12000
91 } {1}
92 do_test 2.2 {
93   execsql {
94     PRAGMA wal_checkpoint;
95     INSERT INTO t1 VALUES(randomblob(750));
96   }
97   file size test.db-wal
98 } {10000}
99 do_test 2.3 {
100   execsql {
101     PRAGMA journal_size_limit = 8000;
102     PRAGMA wal_checkpoint;
103     INSERT INTO t1 VALUES(randomblob(750));
104   }
105   file size test.db-wal
106 } {8000}
108 #-------------------------------------------------------------------------
109 # Test that a checkpoint may be interrupted using sqlite3_interrupt().
110 # And that the error code is SQLITE_NOMEM, not SQLITE_INTERRUPT, if
111 # an OOM error occurs just before the sqlite3_interrupt() call.
113 reset_db
114 db close
115 sqlite3 db test.db -vfs tvfs
116 tvfs filter {}
118 do_execsql_test 3.0 {
119   CREATE TABLE t1(x);
120   PRAGMA journal_mode = wal;
121   WITH s(i) AS (
122     SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
123   )
124   INSERT INTO t1 SELECT randomblob(750) FROM s;
125 } {wal}
127 tvfs filter xWrite
128 tvfs script xWrite
129 set ::cnt 2
130 proc xWrite {method file args} {
131   if {[file tail $file]=="test.db"} {
132     incr ::cnt -1
133     if {$::cnt==0} {
134       sqlite3_interrupt db
135     }
136   }
137   return SQLITE_OK
140 do_catchsql_test 3.1 {
141   PRAGMA wal_checkpoint
142 } {1 interrupted}
144 set ::cnt 2
145 proc xWrite {method file args} {
146   if {[file tail $file]=="test.db"} {
147     incr ::cnt -1
148     if {$::cnt==0} {
149       sqlite3_memdebug_fail 1 -repeat 0
150       # For this test to pass, the following statement must call malloc() at 
151       # least once. Even if the lookaside is enabled.
152       set ::xwrite_stmt_res [catchsql { SELECT hex(randomblob(4000)) }]
153       sqlite3_interrupt db
154     }
155   }
156   return SQLITE_OK
159 set ::xwrite_stmt_res ""
160 do_catchsql_test 3.2 {
161   PRAGMA wal_checkpoint
162 } {1 {out of memory}}
163 do_test 3.2.2 {
164   set ::xwrite_stmt_res
165 } {1 {out of memory}}
166 unset ::xwrite_stmt_res
168 #-------------------------------------------------------------------------
170 reset_db
171 db close
172 do_test 4.0 {
173   sqlite3 db test.db -vfs tvfs
174   execsql {
175     CREATE TABLE t1(x);
176     PRAGMA journal_mode = wal;
177     WITH s(i) AS (
178         SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
179     )
180     INSERT INTO t1 SELECT randomblob(750) FROM s;
181   } db
182 } {wal}
183 db close
185 tvfs filter xShmMap
186 tvfs script xShmMap
187 proc xShmMap {method file args} { 
188   return SQLITE_READONLY 
190 sqlite3 db test.db -vfs tvfs
191 do_catchsql_test 4.1 {
192   SELECT count(*) FROM t1
193 } {1 {attempt to write a readonly database}}
195 set ::cnt 5
196 tvfs filter {xShmMap xShmLock}
197 proc xShmMap {method file name args} { 
198   switch -- $method {
199     xShmMap {  return SQLITE_READONLY }
200     xShmLock {
201       if {$args == "{0 1 lock shared}"} {
202         incr ::cnt -1
203         if {$::cnt>0} { return SQLITE_BUSY }
204       }
205     }
206   }
207   return SQLITE_OK
209 do_catchsql_test 4.2 {
210   SELECT count(*) FROM t1
211 } {1 {attempt to write a readonly database}}
213 #-------------------------------------------------------------------------
215 reset_db
216 db close 
217 sqlite3 db test.db -vfs tvfs
218 tvfs filter {}
219 do_execsql_test 5.0 {
220   PRAGMA auto_vacuum = 0;
221   PRAGMA page_size = 1024;
222   CREATE TABLE t1(x);
223   PRAGMA journal_mode = wal;
224   WITH s(i) AS (
225       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
226   )
227   INSERT INTO t1 SELECT randomblob(750) FROM s;
228 } {wal}
230 do_execsql_test 5.1 {
231   SELECT count(*) FROM t1
232 } {20}
234 do_test 5.2 {
235   vfs_set_readmark db main 1 100
236   vfs_set_readmark db main 2 100
237   vfs_set_readmark db main 3 100
238   vfs_set_readmark db main 4 100
239 } {100}
241 do_execsql_test 5.3 {
242   SELECT count(*) FROM t1
243 } {20}
245 do_test 5.3 {
246   list [vfs_set_readmark db main 1] \
247        [vfs_set_readmark db main 2] \
248        [vfs_set_readmark db main 3] \
249        [vfs_set_readmark db main 4] 
250 } {24 100 100 100}
252 tvfs script xShmLock
253 tvfs filter xShmLock
254 set ::cnt 20
255 proc xShmLock {args} {
256   incr ::cnt -1
257   if {$::cnt>0} { return SQLITE_BUSY }
258   return SQLITE_OK
261 do_test 5.4 {
262   vfs_set_readmark db main 1 100
263   execsql { SELECT count(*) FROM t1 }
264 } {20}
266 vfs_set_readmark db main 1 100
267 vfs_set_readmark db main 2 100
268 vfs_set_readmark db main 3 100
269 vfs_set_readmark db main 4 100
271 tvfs script xShmMapLock
272 tvfs filter {xShmLock xShmMap}
273 proc xShmMapLock {method args} {
274   if {$method=="xShmMap"} {
275     return "SQLITE_READONLY"
276   }
277   return SQLITE_BUSY
280 sqlite3 db2 test.db -vfs tvfs
281 breakpoint
282 do_test 5.5 {
283   list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
284 } {1 {attempt to write a readonly database}}
286 tvfs filter {}
287 vfs_set_readmark db main 1 1
289 do_test 5.6 {
290   list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
291 } {0 20}
292 db2 close
293 db close
295 #-------------------------------------------------------------------------
296 # Cause an SQLITE_PROTOCOL while attempting to restart the wal file.
298 reset_db
299 tvfs filter {}
300 db close
301 sqlite3 db test.db -vfs tvfs
302 do_execsql_test 6.0 {
303   PRAGMA auto_vacuum = 0;
304   PRAGMA page_size = 1024;
305   CREATE TABLE t1(x);
306   PRAGMA journal_mode = wal;
307   WITH s(i) AS (
308       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
309   )
310   INSERT INTO t1 SELECT randomblob(750) FROM s;
311 } {wal}
313 do_test 6.1 {
314   execsql { PRAGMA wal_checkpoint } 
315   set {} {}
316 } {}
318 tvfs filter xShmLock
319 tvfs script xShmLock
320 set ::flag 0
321 proc xShmLock {method file handle spec} {
322   if {$::flag && [lrange $spec 2 end]=="lock shared"} {
323     return SQLITE_BUSY
324   }
325   if {$spec=="3 1 unlock shared"} {
326     set ::flag 1
327   }
328   return SQLITE_OK
331 puts "# WARNING: This next test takes around 12 seconds"
332 do_catchsql_test 6.2 {
333   INSERT INTO t1 VALUES(1);
334 } {1 {locking protocol}}
336 #-------------------------------------------------------------------------
337 # Check that a checkpoint fails if it cannot get the CHECKPOINTER lock
339 reset_db
340 tvfs filter {}
341 db close
342 sqlite3 db test.db -vfs tvfs
343 do_execsql_test 7.0 {
344   PRAGMA auto_vacuum = 0;
345   PRAGMA page_size = 1024;
346   CREATE TABLE t1(x);
347   PRAGMA journal_mode = wal;
348   WITH s(i) AS (
349       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
350   )
351   INSERT INTO t1 SELECT randomblob(750) FROM s;
352 } {wal}
354 tvfs script xShmLock
355 tvfs filter xShmLock
356 proc xShmLock {method file handle spec} {
357   if {$spec=="1 1 lock exclusive"} {
358     return SQLITE_BUSY
359   }
360   return SQLITE_OK
363 do_execsql_test 7.1 {
364   PRAGMA wal_checkpoint
365 } {1 -1 -1}
367 #-------------------------------------------------------------------------
368 # Check that the page cache is correctly flushed if a checkpointer using
369 # a version 2 VFS makes a checkpoint with an out-of-date cache.
371 reset_db
372 testvfs tvfs2 -iversion 2
373 db close
374 sqlite3 db test.db -vfs tvfs2
375 do_execsql_test 8.0 {
376   PRAGMA auto_vacuum = 0;
377   PRAGMA page_size = 1024;
378   CREATE TABLE t1(x);
379   PRAGMA journal_mode = wal;
380   WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
381   INSERT INTO t1 SELECT randomblob(75) FROM s;
382 } {wal}
384 do_execsql_test 8.1 { SELECT count(*) FROM t1 } {20}
386 do_test 8.2 {
387   sqlite3 db2 test.db -vfs tvfs2
388   execsql {
389     INSERT INTO t1 VALUES(randomblob(75));
390   } db2
391   db2 close
392 } {}
394 do_execsql_test 8.3 { 
395   PRAGMA wal_checkpoint;
396   SELECT count(*) FROM t1 
397 } {0 5 5 21}
398 db close
399 tvfs2 delete
401 #-------------------------------------------------------------------------
402 reset_db
403 db close
404 sqlite3 db test.db -vfs tvfs
405 do_execsql_test 9.0 {
406   PRAGMA auto_vacuum = 0;
407   PRAGMA page_size = 1024;
408   CREATE TABLE t1(x);
409   PRAGMA journal_mode = wal;
410   WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
411   INSERT INTO t1 SELECT randomblob(75) FROM s;
412 } {wal}
414 sqlite3 db2 test.db -vfs tvfs
415 tvfs filter {xShmMap xShmLock}
416 tvfs script xShmMap
417 proc xShmMap {method file handle args} {
418   switch -- $method {
419     xShmMap {
420       return "SQLITE_READONLY_CANTINIT"
421     }
422     xShmLock {
423       if {$args=="{3 1 lock shared}"} {
424         return "SQLITE_IOERR"
425       }
426     }
427   }
430 do_test 9.1 {
431   catchsql { SELECT count(*) FROM t1 } db2
432 } {1 {disk I/O error}}
434 db close
435 db2 close
436 tvfs delete
437 finish_test