blob: d6c58656f0d8f2485538855c8fecc8b95ab0ca31 [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#
15
16set testdir [file dirname $argv0]
17source $testdir/tester.tcl
18source $testdir/lock_common.tcl
19source $testdir/malloc_common.tcl
20source $testdir/wal_common.tcl
21set testprefix walvfs
22
23ifcapable !wal {finish_test ; return }
24
25db close
26testvfs tvfs
27tvfs script xSync
28tvfs filter xSync
29set ::sync_count 0
30proc xSync {method file args} {
31 if {[file tail $file]=="test.db-wal"} {
32 incr ::sync_count
33 }
34}
35
dan89dec012018-12-26 17:49:57 +000036
dan8d69a582018-12-22 20:32:28 +000037#-------------------------------------------------------------------------
38# Test that if IOCAP_SEQUENTIAL is set, the wal-header is not synced to
39# disk immediately after it is written.
40#
41sqlite3 db test.db -vfs tvfs
42do_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}
52
53set ::sync_count 0
54do_test 1.1 {
55 execsql { INSERT INTO t1 VALUES(10, 11, 12) }
56 set ::sync_count
57} 1
58
59db close
60tvfs devchar sequential
61sqlite3 db test.db -vfs tvfs
62do_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}
68
69set ::sync_count 0
70do_test 1.3 {
71 execsql { INSERT INTO t1 VALUES(10, 11, 12) }
72 set ::sync_count
73} 0
74
75#-------------------------------------------------------------------------
76# Test that "PRAGMA journal_size_limit" works in wal mode.
77#
78reset_db
79do_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}
88do_test 2.1 {
89 expr [file size test.db-wal]>12000
90} {1}
91do_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}
98do_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}
106
107#-------------------------------------------------------------------------
108# Test that a checkpoint may be interrupted using sqlite3_interrupt().
dan89dec012018-12-26 17:49:57 +0000109# And that the error code is SQLITE_NOMEM, not SQLITE_INTERRUPT, if
110# an OOM error occurs just before the sqlite3_interrupt() call.
dan8d69a582018-12-22 20:32:28 +0000111#
112reset_db
113db close
114sqlite3 db test.db -vfs tvfs
115tvfs filter {}
116
117do_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}
125
126tvfs filter xWrite
127tvfs script xWrite
128set ::cnt 2
129proc 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
137}
138
139do_catchsql_test 3.1 {
140 PRAGMA wal_checkpoint
141} {1 interrupted}
142
dan89dec012018-12-26 17:49:57 +0000143set ::cnt 2
144proc xWrite {method file args} {
145 if {[file tail $file]=="test.db"} {
146 incr ::cnt -1
147 if {$::cnt==0} {
daned0af522020-08-10 11:21:48 +0000148 sqlite3_memdebug_fail 1 -repeat 0
danee822182020-12-04 16:26:25 +0000149 # 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)) }]
dan89dec012018-12-26 17:49:57 +0000152 sqlite3_interrupt db
153 }
154 }
155 return SQLITE_OK
156}
157
danee822182020-12-04 16:26:25 +0000158set ::xwrite_stmt_res ""
dan89dec012018-12-26 17:49:57 +0000159do_catchsql_test 3.2 {
160 PRAGMA wal_checkpoint
161} {1 {out of memory}}
danee822182020-12-04 16:26:25 +0000162do_test 3.2.2 {
163 set ::xwrite_stmt_res
164} {1 {out of memory}}
165unset ::xwrite_stmt_res
dan89dec012018-12-26 17:49:57 +0000166
dan8d69a582018-12-22 20:32:28 +0000167#-------------------------------------------------------------------------
168#
169reset_db
170db close
171do_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}
182db close
183
184tvfs filter xShmMap
185tvfs script xShmMap
186proc xShmMap {method file args} {
187 return SQLITE_READONLY
188}
189sqlite3 db test.db -vfs tvfs
190do_catchsql_test 4.1 {
191 SELECT count(*) FROM t1
192} {1 {attempt to write a readonly database}}
193
194set ::cnt 5
195tvfs filter {xShmMap xShmLock}
196proc 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
207}
208do_catchsql_test 4.2 {
209 SELECT count(*) FROM t1
210} {1 {attempt to write a readonly database}}
211
dan76e49902018-12-24 18:51:13 +0000212#-------------------------------------------------------------------------
213#
214reset_db
215db close
216sqlite3 db test.db -vfs tvfs
217tvfs filter {}
218do_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}
228
229do_execsql_test 5.1 {
230 SELECT count(*) FROM t1
231} {20}
232
233do_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}
239
240do_execsql_test 5.3 {
241 SELECT count(*) FROM t1
242} {20}
243
244do_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}
250
251tvfs script xShmLock
252tvfs filter xShmLock
253set ::cnt 20
254proc xShmLock {args} {
255 incr ::cnt -1
256 if {$::cnt>0} { return SQLITE_BUSY }
257 return SQLITE_OK
258}
259
260do_test 5.4 {
261 vfs_set_readmark db main 1 100
262 execsql { SELECT count(*) FROM t1 }
263} {20}
264
dan89dec012018-12-26 17:49:57 +0000265vfs_set_readmark db main 1 100
266vfs_set_readmark db main 2 100
267vfs_set_readmark db main 3 100
268vfs_set_readmark db main 4 100
269
270tvfs script xShmMapLock
271tvfs filter {xShmLock xShmMap}
272proc xShmMapLock {method args} {
273 if {$method=="xShmMap"} {
274 return "SQLITE_READONLY"
275 }
276 return SQLITE_BUSY
277}
278
279sqlite3 db2 test.db -vfs tvfs
280breakpoint
281do_test 5.5 {
282 list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
283} {1 {attempt to write a readonly database}}
284
285tvfs filter {}
286vfs_set_readmark db main 1 1
287
288do_test 5.6 {
289 list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
290} {0 20}
291db2 close
292db close
293
dan76e49902018-12-24 18:51:13 +0000294#-------------------------------------------------------------------------
295# Cause an SQLITE_PROTOCOL while attempting to restart the wal file.
dan92107a32018-12-24 20:00:27 +0000296#
dan76e49902018-12-24 18:51:13 +0000297reset_db
298tvfs filter {}
299db close
300sqlite3 db test.db -vfs tvfs
301do_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}
311
312do_test 6.1 {
313 execsql { PRAGMA wal_checkpoint }
314 set {} {}
315} {}
316
317tvfs filter xShmLock
dan89dec012018-12-26 17:49:57 +0000318tvfs script xShmLock
dan76e49902018-12-24 18:51:13 +0000319set ::flag 0
320proc 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
328}
329
330puts "# WARNING: This next test takes around 12 seconds"
331do_catchsql_test 6.2 {
332 INSERT INTO t1 VALUES(1);
333} {1 {locking protocol}}
334
dan92107a32018-12-24 20:00:27 +0000335#-------------------------------------------------------------------------
336# Check that a checkpoint fails if it cannot get the CHECKPOINTER lock
337#
338reset_db
339tvfs filter {}
340db close
341sqlite3 db test.db -vfs tvfs
342do_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}
352
353tvfs script xShmLock
354tvfs filter xShmLock
355proc xShmLock {method file handle spec} {
356 if {$spec=="1 1 lock exclusive"} {
357 return SQLITE_BUSY
358 }
359 return SQLITE_OK
360}
361
362do_execsql_test 7.1 {
363 PRAGMA wal_checkpoint
364} {1 -1 -1}
365
dan89dec012018-12-26 17:49:57 +0000366#-------------------------------------------------------------------------
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.
369#
370reset_db
371testvfs tvfs2 -iversion 2
dan8d69a582018-12-22 20:32:28 +0000372db close
dan89dec012018-12-26 17:49:57 +0000373sqlite3 db test.db -vfs tvfs2
374do_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}
382
383do_execsql_test 8.1 { SELECT count(*) FROM t1 } {20}
384
385do_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} {}
392
393do_execsql_test 8.3 {
394 PRAGMA wal_checkpoint;
395 SELECT count(*) FROM t1
396} {0 5 5 21}
dan83420822019-11-20 16:10:40 +0000397db close
dan89dec012018-12-26 17:49:57 +0000398tvfs2 delete
399
400#-------------------------------------------------------------------------
401reset_db
402db close
403sqlite3 db test.db -vfs tvfs
404do_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}
412
413sqlite3 db2 test.db -vfs tvfs
414tvfs filter {xShmMap xShmLock}
415tvfs script xShmMap
416proc 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 }
427}
428
429do_test 9.1 {
430 catchsql { SELECT count(*) FROM t1 } db2
431} {1 {disk I/O error}}
432
433db close
434db2 close
dan8d69a582018-12-22 20:32:28 +0000435tvfs delete
436finish_test