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