blob: d32cbd73f1b70bb672a640abe6c3fe0d2d64ae41 [file] [log] [blame]
dan8d69a582018-12-22 20:32:28 +00001# 2018 December 23
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# 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.
14#
dancf2ad7a2022-07-18 19:32:30 +000015# TESTRUNNER: slow
dan8d69a582018-12-22 20:32:28 +000016
17set testdir [file dirname $argv0]
18source $testdir/tester.tcl
19source $testdir/lock_common.tcl
20source $testdir/malloc_common.tcl
21source $testdir/wal_common.tcl
22set testprefix walvfs
23
24ifcapable !wal {finish_test ; return }
25
26db close
27testvfs tvfs
28tvfs script xSync
29tvfs filter xSync
30set ::sync_count 0
31proc xSync {method file args} {
32 if {[file tail $file]=="test.db-wal"} {
33 incr ::sync_count
34 }
35}
36
dan89dec012018-12-26 17:49:57 +000037
dan8d69a582018-12-22 20:32:28 +000038#-------------------------------------------------------------------------
39# Test that if IOCAP_SEQUENTIAL is set, the wal-header is not synced to
40# disk immediately after it is written.
41#
42sqlite3 db test.db -vfs tvfs
43do_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}
53
54set ::sync_count 0
55do_test 1.1 {
56 execsql { INSERT INTO t1 VALUES(10, 11, 12) }
57 set ::sync_count
58} 1
59
60db close
61tvfs devchar sequential
62sqlite3 db test.db -vfs tvfs
63do_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}
69
70set ::sync_count 0
71do_test 1.3 {
72 execsql { INSERT INTO t1 VALUES(10, 11, 12) }
73 set ::sync_count
74} 0
75
76#-------------------------------------------------------------------------
77# Test that "PRAGMA journal_size_limit" works in wal mode.
78#
79reset_db
80do_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}
89do_test 2.1 {
90 expr [file size test.db-wal]>12000
91} {1}
92do_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}
99do_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}
107
108#-------------------------------------------------------------------------
109# Test that a checkpoint may be interrupted using sqlite3_interrupt().
dan89dec012018-12-26 17:49:57 +0000110# And that the error code is SQLITE_NOMEM, not SQLITE_INTERRUPT, if
111# an OOM error occurs just before the sqlite3_interrupt() call.
dan8d69a582018-12-22 20:32:28 +0000112#
113reset_db
114db close
115sqlite3 db test.db -vfs tvfs
116tvfs filter {}
117
118do_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}
126
127tvfs filter xWrite
128tvfs script xWrite
129set ::cnt 2
130proc 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
138}
139
140do_catchsql_test 3.1 {
141 PRAGMA wal_checkpoint
142} {1 interrupted}
143
dan89dec012018-12-26 17:49:57 +0000144set ::cnt 2
145proc xWrite {method file args} {
146 if {[file tail $file]=="test.db"} {
147 incr ::cnt -1
148 if {$::cnt==0} {
daned0af522020-08-10 11:21:48 +0000149 sqlite3_memdebug_fail 1 -repeat 0
danee822182020-12-04 16:26:25 +0000150 # 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)) }]
dan89dec012018-12-26 17:49:57 +0000153 sqlite3_interrupt db
154 }
155 }
156 return SQLITE_OK
157}
158
danee822182020-12-04 16:26:25 +0000159set ::xwrite_stmt_res ""
dan89dec012018-12-26 17:49:57 +0000160do_catchsql_test 3.2 {
161 PRAGMA wal_checkpoint
162} {1 {out of memory}}
danee822182020-12-04 16:26:25 +0000163do_test 3.2.2 {
164 set ::xwrite_stmt_res
165} {1 {out of memory}}
166unset ::xwrite_stmt_res
dan89dec012018-12-26 17:49:57 +0000167
dan8d69a582018-12-22 20:32:28 +0000168#-------------------------------------------------------------------------
169#
170reset_db
171db close
172do_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}
183db close
184
185tvfs filter xShmMap
186tvfs script xShmMap
187proc xShmMap {method file args} {
188 return SQLITE_READONLY
189}
190sqlite3 db test.db -vfs tvfs
191do_catchsql_test 4.1 {
192 SELECT count(*) FROM t1
193} {1 {attempt to write a readonly database}}
194
195set ::cnt 5
196tvfs filter {xShmMap xShmLock}
197proc 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
208}
209do_catchsql_test 4.2 {
210 SELECT count(*) FROM t1
211} {1 {attempt to write a readonly database}}
212
dan76e49902018-12-24 18:51:13 +0000213#-------------------------------------------------------------------------
214#
215reset_db
216db close
217sqlite3 db test.db -vfs tvfs
218tvfs filter {}
219do_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}
229
230do_execsql_test 5.1 {
231 SELECT count(*) FROM t1
232} {20}
233
234do_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}
240
241do_execsql_test 5.3 {
242 SELECT count(*) FROM t1
243} {20}
244
245do_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}
251
252tvfs script xShmLock
253tvfs filter xShmLock
254set ::cnt 20
255proc xShmLock {args} {
256 incr ::cnt -1
257 if {$::cnt>0} { return SQLITE_BUSY }
258 return SQLITE_OK
259}
260
261do_test 5.4 {
262 vfs_set_readmark db main 1 100
263 execsql { SELECT count(*) FROM t1 }
264} {20}
265
dan89dec012018-12-26 17:49:57 +0000266vfs_set_readmark db main 1 100
267vfs_set_readmark db main 2 100
268vfs_set_readmark db main 3 100
269vfs_set_readmark db main 4 100
270
271tvfs script xShmMapLock
272tvfs filter {xShmLock xShmMap}
273proc xShmMapLock {method args} {
274 if {$method=="xShmMap"} {
275 return "SQLITE_READONLY"
276 }
277 return SQLITE_BUSY
278}
279
280sqlite3 db2 test.db -vfs tvfs
281breakpoint
282do_test 5.5 {
283 list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
284} {1 {attempt to write a readonly database}}
285
286tvfs filter {}
287vfs_set_readmark db main 1 1
288
289do_test 5.6 {
290 list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
291} {0 20}
292db2 close
293db close
294
dan76e49902018-12-24 18:51:13 +0000295#-------------------------------------------------------------------------
296# Cause an SQLITE_PROTOCOL while attempting to restart the wal file.
dan92107a32018-12-24 20:00:27 +0000297#
dan76e49902018-12-24 18:51:13 +0000298reset_db
299tvfs filter {}
300db close
301sqlite3 db test.db -vfs tvfs
302do_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}
312
313do_test 6.1 {
314 execsql { PRAGMA wal_checkpoint }
315 set {} {}
316} {}
317
318tvfs filter xShmLock
dan89dec012018-12-26 17:49:57 +0000319tvfs script xShmLock
dan76e49902018-12-24 18:51:13 +0000320set ::flag 0
321proc 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
329}
330
331puts "# WARNING: This next test takes around 12 seconds"
332do_catchsql_test 6.2 {
333 INSERT INTO t1 VALUES(1);
334} {1 {locking protocol}}
335
dan92107a32018-12-24 20:00:27 +0000336#-------------------------------------------------------------------------
337# Check that a checkpoint fails if it cannot get the CHECKPOINTER lock
338#
339reset_db
340tvfs filter {}
341db close
342sqlite3 db test.db -vfs tvfs
343do_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}
353
354tvfs script xShmLock
355tvfs filter xShmLock
356proc xShmLock {method file handle spec} {
357 if {$spec=="1 1 lock exclusive"} {
358 return SQLITE_BUSY
359 }
360 return SQLITE_OK
361}
362
363do_execsql_test 7.1 {
364 PRAGMA wal_checkpoint
365} {1 -1 -1}
366
dan89dec012018-12-26 17:49:57 +0000367#-------------------------------------------------------------------------
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.
370#
371reset_db
372testvfs tvfs2 -iversion 2
dan8d69a582018-12-22 20:32:28 +0000373db close
dan89dec012018-12-26 17:49:57 +0000374sqlite3 db test.db -vfs tvfs2
375do_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}
383
384do_execsql_test 8.1 { SELECT count(*) FROM t1 } {20}
385
386do_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} {}
393
394do_execsql_test 8.3 {
395 PRAGMA wal_checkpoint;
396 SELECT count(*) FROM t1
397} {0 5 5 21}
dan83420822019-11-20 16:10:40 +0000398db close
dan89dec012018-12-26 17:49:57 +0000399tvfs2 delete
400
401#-------------------------------------------------------------------------
402reset_db
403db close
404sqlite3 db test.db -vfs tvfs
405do_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}
413
414sqlite3 db2 test.db -vfs tvfs
415tvfs filter {xShmMap xShmLock}
416tvfs script xShmMap
417proc 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 }
428}
429
430do_test 9.1 {
431 catchsql { SELECT count(*) FROM t1 } db2
432} {1 {disk I/O error}}
433
434db close
435db2 close
dan8d69a582018-12-22 20:32:28 +0000436tvfs delete
437finish_test