xref: /sqlite-3.40.0/test/walvfs.test (revision cf2ad7ae)
1# 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# TESTRUNNER: slow
16
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
37
38#-------------------------------------------------------------------------
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().
110# And that the error code is SQLITE_NOMEM, not SQLITE_INTERRUPT, if
111# an OOM error occurs just before the sqlite3_interrupt() call.
112#
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
144set ::cnt 2
145proc 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
157}
158
159set ::xwrite_stmt_res ""
160do_catchsql_test 3.2 {
161  PRAGMA wal_checkpoint
162} {1 {out of memory}}
163do_test 3.2.2 {
164  set ::xwrite_stmt_res
165} {1 {out of memory}}
166unset ::xwrite_stmt_res
167
168#-------------------------------------------------------------------------
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
213#-------------------------------------------------------------------------
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
266vfs_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
295#-------------------------------------------------------------------------
296# Cause an SQLITE_PROTOCOL while attempting to restart the wal file.
297#
298reset_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
319tvfs script xShmLock
320set ::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
336#-------------------------------------------------------------------------
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
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.
370#
371reset_db
372testvfs tvfs2 -iversion 2
373db close
374sqlite3 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}
398db close
399tvfs2 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
436tvfs delete
437finish_test
438