Revert "change compilation / amalgamation order of sqlcipher sources"
[sqlcipher.git] / test / walvfs.test
blobd6c58656f0d8f2485538855c8fecc8b95ab0ca31
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       # For this test to pass, the following statement must call malloc() at 
150       # least once. Even if the lookaside is enabled.
151       set ::xwrite_stmt_res [catchsql { SELECT hex(randomblob(4000)) }]
152       sqlite3_interrupt db
153     }
154   }
155   return SQLITE_OK
158 set ::xwrite_stmt_res ""
159 do_catchsql_test 3.2 {
160   PRAGMA wal_checkpoint
161 } {1 {out of memory}}
162 do_test 3.2.2 {
163   set ::xwrite_stmt_res
164 } {1 {out of memory}}
165 unset ::xwrite_stmt_res
167 #-------------------------------------------------------------------------
169 reset_db
170 db close
171 do_test 4.0 {
172   sqlite3 db test.db -vfs tvfs
173   execsql {
174     CREATE TABLE t1(x);
175     PRAGMA journal_mode = wal;
176     WITH s(i) AS (
177         SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
178     )
179     INSERT INTO t1 SELECT randomblob(750) FROM s;
180   } db
181 } {wal}
182 db close
184 tvfs filter xShmMap
185 tvfs script xShmMap
186 proc xShmMap {method file args} { 
187   return SQLITE_READONLY 
189 sqlite3 db test.db -vfs tvfs
190 do_catchsql_test 4.1 {
191   SELECT count(*) FROM t1
192 } {1 {attempt to write a readonly database}}
194 set ::cnt 5
195 tvfs filter {xShmMap xShmLock}
196 proc xShmMap {method file name args} { 
197   switch -- $method {
198     xShmMap {  return SQLITE_READONLY }
199     xShmLock {
200       if {$args == "{0 1 lock shared}"} {
201         incr ::cnt -1
202         if {$::cnt>0} { return SQLITE_BUSY }
203       }
204     }
205   }
206   return SQLITE_OK
208 do_catchsql_test 4.2 {
209   SELECT count(*) FROM t1
210 } {1 {attempt to write a readonly database}}
212 #-------------------------------------------------------------------------
214 reset_db
215 db close 
216 sqlite3 db test.db -vfs tvfs
217 tvfs filter {}
218 do_execsql_test 5.0 {
219   PRAGMA auto_vacuum = 0;
220   PRAGMA page_size = 1024;
221   CREATE TABLE t1(x);
222   PRAGMA journal_mode = wal;
223   WITH s(i) AS (
224       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
225   )
226   INSERT INTO t1 SELECT randomblob(750) FROM s;
227 } {wal}
229 do_execsql_test 5.1 {
230   SELECT count(*) FROM t1
231 } {20}
233 do_test 5.2 {
234   vfs_set_readmark db main 1 100
235   vfs_set_readmark db main 2 100
236   vfs_set_readmark db main 3 100
237   vfs_set_readmark db main 4 100
238 } {100}
240 do_execsql_test 5.3 {
241   SELECT count(*) FROM t1
242 } {20}
244 do_test 5.3 {
245   list [vfs_set_readmark db main 1] \
246        [vfs_set_readmark db main 2] \
247        [vfs_set_readmark db main 3] \
248        [vfs_set_readmark db main 4] 
249 } {24 100 100 100}
251 tvfs script xShmLock
252 tvfs filter xShmLock
253 set ::cnt 20
254 proc xShmLock {args} {
255   incr ::cnt -1
256   if {$::cnt>0} { return SQLITE_BUSY }
257   return SQLITE_OK
260 do_test 5.4 {
261   vfs_set_readmark db main 1 100
262   execsql { SELECT count(*) FROM t1 }
263 } {20}
265 vfs_set_readmark db main 1 100
266 vfs_set_readmark db main 2 100
267 vfs_set_readmark db main 3 100
268 vfs_set_readmark db main 4 100
270 tvfs script xShmMapLock
271 tvfs filter {xShmLock xShmMap}
272 proc xShmMapLock {method args} {
273   if {$method=="xShmMap"} {
274     return "SQLITE_READONLY"
275   }
276   return SQLITE_BUSY
279 sqlite3 db2 test.db -vfs tvfs
280 breakpoint
281 do_test 5.5 {
282   list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
283 } {1 {attempt to write a readonly database}}
285 tvfs filter {}
286 vfs_set_readmark db main 1 1
288 do_test 5.6 {
289   list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
290 } {0 20}
291 db2 close
292 db close
294 #-------------------------------------------------------------------------
295 # Cause an SQLITE_PROTOCOL while attempting to restart the wal file.
297 reset_db
298 tvfs filter {}
299 db close
300 sqlite3 db test.db -vfs tvfs
301 do_execsql_test 6.0 {
302   PRAGMA auto_vacuum = 0;
303   PRAGMA page_size = 1024;
304   CREATE TABLE t1(x);
305   PRAGMA journal_mode = wal;
306   WITH s(i) AS (
307       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
308   )
309   INSERT INTO t1 SELECT randomblob(750) FROM s;
310 } {wal}
312 do_test 6.1 {
313   execsql { PRAGMA wal_checkpoint } 
314   set {} {}
315 } {}
317 tvfs filter xShmLock
318 tvfs script xShmLock
319 set ::flag 0
320 proc xShmLock {method file handle spec} {
321   if {$::flag && [lrange $spec 2 end]=="lock shared"} {
322     return SQLITE_BUSY
323   }
324   if {$spec=="3 1 unlock shared"} {
325     set ::flag 1
326   }
327   return SQLITE_OK
330 puts "# WARNING: This next test takes around 12 seconds"
331 do_catchsql_test 6.2 {
332   INSERT INTO t1 VALUES(1);
333 } {1 {locking protocol}}
335 #-------------------------------------------------------------------------
336 # Check that a checkpoint fails if it cannot get the CHECKPOINTER lock
338 reset_db
339 tvfs filter {}
340 db close
341 sqlite3 db test.db -vfs tvfs
342 do_execsql_test 7.0 {
343   PRAGMA auto_vacuum = 0;
344   PRAGMA page_size = 1024;
345   CREATE TABLE t1(x);
346   PRAGMA journal_mode = wal;
347   WITH s(i) AS (
348       SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
349   )
350   INSERT INTO t1 SELECT randomblob(750) FROM s;
351 } {wal}
353 tvfs script xShmLock
354 tvfs filter xShmLock
355 proc xShmLock {method file handle spec} {
356   if {$spec=="1 1 lock exclusive"} {
357     return SQLITE_BUSY
358   }
359   return SQLITE_OK
362 do_execsql_test 7.1 {
363   PRAGMA wal_checkpoint
364 } {1 -1 -1}
366 #-------------------------------------------------------------------------
367 # Check that the page cache is correctly flushed if a checkpointer using
368 # a version 2 VFS makes a checkpoint with an out-of-date cache.
370 reset_db
371 testvfs tvfs2 -iversion 2
372 db close
373 sqlite3 db test.db -vfs tvfs2
374 do_execsql_test 8.0 {
375   PRAGMA auto_vacuum = 0;
376   PRAGMA page_size = 1024;
377   CREATE TABLE t1(x);
378   PRAGMA journal_mode = wal;
379   WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
380   INSERT INTO t1 SELECT randomblob(75) FROM s;
381 } {wal}
383 do_execsql_test 8.1 { SELECT count(*) FROM t1 } {20}
385 do_test 8.2 {
386   sqlite3 db2 test.db -vfs tvfs2
387   execsql {
388     INSERT INTO t1 VALUES(randomblob(75));
389   } db2
390   db2 close
391 } {}
393 do_execsql_test 8.3 { 
394   PRAGMA wal_checkpoint;
395   SELECT count(*) FROM t1 
396 } {0 5 5 21}
397 db close
398 tvfs2 delete
400 #-------------------------------------------------------------------------
401 reset_db
402 db close
403 sqlite3 db test.db -vfs tvfs
404 do_execsql_test 9.0 {
405   PRAGMA auto_vacuum = 0;
406   PRAGMA page_size = 1024;
407   CREATE TABLE t1(x);
408   PRAGMA journal_mode = wal;
409   WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
410   INSERT INTO t1 SELECT randomblob(75) FROM s;
411 } {wal}
413 sqlite3 db2 test.db -vfs tvfs
414 tvfs filter {xShmMap xShmLock}
415 tvfs script xShmMap
416 proc xShmMap {method file handle args} {
417   switch -- $method {
418     xShmMap {
419       return "SQLITE_READONLY_CANTINIT"
420     }
421     xShmLock {
422       if {$args=="{3 1 lock shared}"} {
423         return "SQLITE_IOERR"
424       }
425     }
426   }
429 do_test 9.1 {
430   catchsql { SELECT count(*) FROM t1 } db2
431 } {1 {disk I/O error}}
433 db close
434 db2 close
435 tvfs delete
436 finish_test