xref: /sqlite-3.40.0/test/walfault.test (revision 65f2ac58)
176ed3bc0Sdan# 2010 May 03
276ed3bc0Sdan#
376ed3bc0Sdan# The author disclaims copyright to this source code.  In place of
476ed3bc0Sdan# a legal notice, here is a blessing:
576ed3bc0Sdan#
676ed3bc0Sdan#    May you do good and not evil.
776ed3bc0Sdan#    May you find forgiveness for yourself and forgive others.
876ed3bc0Sdan#    May you share freely, never taking more than you give.
976ed3bc0Sdan#
1076ed3bc0Sdan#***********************************************************************
1176ed3bc0Sdan# This file implements regression tests for SQLite library.  The
1276ed3bc0Sdan# focus of this file is testing the operation of the library in
1376ed3bc0Sdan# "PRAGMA journal_mode=WAL" mode.
1476ed3bc0Sdan#
1576ed3bc0Sdan
1676ed3bc0Sdanset testdir [file dirname $argv0]
1776ed3bc0Sdansource $testdir/tester.tcl
1876ed3bc0Sdansource $testdir/malloc_common.tcl
1976ed3bc0Sdan
2076ed3bc0Sdanifcapable !wal {finish_test ; return }
2176ed3bc0Sdan
2276ed3bc0Sdando_malloc_test walfault-oom-1 -sqlbody {
2376ed3bc0Sdan  PRAGMA journal_mode = WAL;
2476ed3bc0Sdan  CREATE TABLE t1(a, b);
2576ed3bc0Sdan  INSERT INTO t1 VALUES(1, 2);
26ef378025Sdan  PRAGMA wal_checkpoint;
2776ed3bc0Sdan}
2876ed3bc0Sdan
295e9e482fSdando_malloc_test walfault-oom-2 -tclprep {
305e9e482fSdan  execsql {
315e9e482fSdan    PRAGMA journal_mode = WAL;
325e9e482fSdan    BEGIN;
335e9e482fSdan      CREATE TABLE x(y, z, UNIQUE(y, z));
345e9e482fSdan      INSERT INTO x VALUES(randomblob(100), randomblob(100));
355e9e482fSdan    COMMIT;
365e9e482fSdan    PRAGMA wal_checkpoint;
375e9e482fSdan
385e9e482fSdan    INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
395e9e482fSdan    INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
405e9e482fSdan    INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
415e9e482fSdan  }
425e9e482fSdan  file copy -force test.db testX.db
435e9e482fSdan  file copy -force test.db-wal testX.db-wal
445e9e482fSdan  db close
455e9e482fSdan  file rename -force testX.db test.db
465e9e482fSdan  file rename -force testX.db-wal test.db-wal
475e9e482fSdan
485e9e482fSdan  sqlite3 db test.db
495e9e482fSdan  sqlite3_extended_result_codes db 1
505e9e482fSdan  sqlite3_db_config_lookaside db 0 0 0
515e9e482fSdan} -sqlbody {
525e9e482fSdan  SELECT count(*) FROM x;
535e9e482fSdan}
545e9e482fSdan
559a6b4e9aSdando_ioerr_test walfault-ioerr-1 -sqlprep {
569a6b4e9aSdan  PRAGMA auto_vacuum = 1;
579a6b4e9aSdan  PRAGMA journal_mode = WAL;
589a6b4e9aSdan  CREATE TABLE abc(a PRIMARY KEY);
599a6b4e9aSdan  INSERT INTO abc VALUES(randomblob(1500));
609a6b4e9aSdan} -sqlbody {
619a6b4e9aSdan  DELETE FROM abc;
629a6b4e9aSdan  PRAGMA wal_checkpoint;
639a6b4e9aSdan}
649a6b4e9aSdan
65c7991bdfSdan# A [testvfs] callback for the VFS created by [do_shmfault_test]. This
668f6097c2Sdan# callback injects SQLITE_IOERR faults into methods for which an entry
678f6097c2Sdan# in array ::shmfault_ioerr_methods is defined. For example, to enable
688f6097c2Sdan# errors in xShmOpen:
69c7991bdfSdan#
708f6097c2Sdan#   set ::shmfault_ioerr_methods(xShmOpen) 1
71c7991bdfSdan#
72c7991bdfSdan# Faults are not injected into xShmRelease, xShmClose or xShmLock method
73c7991bdfSdan# calls. The global tcl variables used are:
74c7991bdfSdan#
75c7991bdfSdan#   $::shmfault_ioerr_countdown
76c7991bdfSdan#   $::shmfault_ioerr_persist
778f6097c2Sdan#   $::shmfault_ioerr_methods
78c7991bdfSdan#
79c7991bdfSdanproc shmfault_vfs_cb {method args} {
80c7991bdfSdan
81c7991bdfSdan  # If ::shmfault_ioerr_countdown is not set, always return SQLITE_OK.
82c7991bdfSdan  #
83c7991bdfSdan  if {[info exists ::shmfault_ioerr_countdown]==0} { return SQLITE_OK }
84c7991bdfSdan
858f6097c2Sdan  if {[info exists ::shmfault_ioerr_methods($method)]} {
86c7991bdfSdan    incr ::shmfault_ioerr_countdown -1
87c7991bdfSdan    if { ($::shmfault_ioerr_countdown==0)
88c7991bdfSdan      || ($::shmfault_ioerr_countdown<=0 && $::shmfault_ioerr_persist)
89c7991bdfSdan    } {
90c7991bdfSdan      return SQLITE_IOERR
91c7991bdfSdan    }
92c7991bdfSdan  }
93c7991bdfSdan  return SQLITE_OK
94c7991bdfSdan}
95c7991bdfSdan
968f6097c2Sdan# Options are:
978f6097c2Sdan#
988f6097c2Sdan#   -tclprep TCL
998f6097c2Sdan#   -sqlprep SQL
1008f6097c2Sdan#   -sqlbody SQL
1018f6097c2Sdan#
102c7991bdfSdanproc do_shmfault_test {name args} {
1038f6097c2Sdan
1048f6097c2Sdan  set A(-tclprep) "sqlite3 db test.db -vfs shmfault"
1058f6097c2Sdan  set A(-sqlprep) ""
1068f6097c2Sdan  set A(-sqlbody) ""
1078f6097c2Sdan  set A(-methods) [list xShmGet xShmOpen xShmSize]
108d41a29a6Sdan  set A(-coverageonly) 0
109c7991bdfSdan  array set A $args
110c7991bdfSdan
111c7991bdfSdan  # Create a VFS to use:
112c7991bdfSdan  testvfs shmfault shmfault_vfs_cb
113c7991bdfSdan
1148f6097c2Sdan  unset -nocomplain ::shmfault_ioerr_methods
1158f6097c2Sdan  foreach m $A(-methods) { set ::shmfault_ioerr_methods($m) 1 }
1168f6097c2Sdan
117c7991bdfSdan  foreach mode {transient persistent} {
118c7991bdfSdan    set ::shmfault_ioerr_persist [expr {$mode == "persistent"}]
1198f6097c2Sdan
120c7991bdfSdan    for {set nDelay 1} {$nDelay < 10000} {incr nDelay} {
121c7991bdfSdan
122c7991bdfSdan      file delete -force test.db test.db-wal test.db-journal
123c7991bdfSdan
1248f6097c2Sdan      eval $A(-tclprep)
1258f6097c2Sdan      db eval $A(-sqlprep)
1268f6097c2Sdan
1278f6097c2Sdan      set ::shmfault_ioerr_countdown $nDelay
1288f6097c2Sdan      set rc [catch { db eval $A(-sqlbody) } msg]
129c7991bdfSdan      set hit_error [expr {$::shmfault_ioerr_countdown<=0}]
130c7991bdfSdan      unset ::shmfault_ioerr_countdown
1318f6097c2Sdan
132c7991bdfSdan      catch { db close }
133c7991bdfSdan
134d41a29a6Sdan      if {$A(-coverageonly)} { set rc $hit_error }
135c7991bdfSdan      do_test $name-$mode.$nDelay.1 [list set {} $hit_error] $rc
136c7991bdfSdan
137c7991bdfSdan      if {$hit_error==0} break
138c7991bdfSdan    }
139c7991bdfSdan  }
140c7991bdfSdan
141c7991bdfSdan  shmfault delete
142c7991bdfSdan}
143c7991bdfSdan
144c7991bdfSdando_shmfault_test walfault-shm-1 -sqlbody {
145c7991bdfSdan  PRAGMA journal_mode = WAL;
146c7991bdfSdan  CREATE TABLE t1(a PRIMARY KEY, b);
147c7991bdfSdan  INSERT INTO t1 VALUES('a', 'b');
148c7991bdfSdan  PRAGMA wal_checkpoint;
149c7991bdfSdan}
15076ed3bc0Sdan
1518f6097c2Sdando_shmfault_test walfault-shm-2 -methods xShmSize -sqlprep {
1528f6097c2Sdan  PRAGMA page_size = 512;
1538f6097c2Sdan  PRAGMA journal_mode = WAL;
1548f6097c2Sdan  PRAGMA wal_autocheckpoint = 0;
1558f6097c2Sdan} -sqlbody {
1568f6097c2Sdan  CREATE TABLE t1(x);
1578f6097c2Sdan  BEGIN;
1588f6097c2Sdan    INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
1598f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
1608f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
1618f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
1628f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
1638f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
1648f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
1658f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
1668f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
1678f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
1688f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
1698f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
1708f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
1718f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
1728f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
1738f6097c2Sdan  COMMIT;
1748f6097c2Sdan}
1758f6097c2Sdan
1768f6097c2Sdando_shmfault_test walfault-shm-3 -methods xShmSize -tclprep {
1778f6097c2Sdan  sqlite3 db test.db -vfs shmfault
1788f6097c2Sdan  unset -nocomplain ::shmfault_ioerr_countdown
1798f6097c2Sdan  db eval {
1808f6097c2Sdan    PRAGMA page_size = 512;
1818f6097c2Sdan    PRAGMA journal_mode = WAL;
1828f6097c2Sdan    PRAGMA wal_autocheckpoint = 0;
1838f6097c2Sdan    CREATE TABLE t1(x);
1848f6097c2Sdan    BEGIN;
1858f6097c2Sdan      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
1868f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
1878f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
1888f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
1898f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
1908f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
1918f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
1928f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
1938f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
1948f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
1958f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
1968f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
1978f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
1988f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
1998f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
2008f6097c2Sdan    COMMIT;
2018f6097c2Sdan  }
2028f6097c2Sdan
2038f6097c2Sdan  set ::shmfault_ioerr_countdown 1
2048f6097c2Sdan  set ::shmfault_ioerr_methods(xShmGet) 1
2058f6097c2Sdan  db close
2068f6097c2Sdan  unset ::shmfault_ioerr_methods(xShmGet)
2078f6097c2Sdan  if {[file exists test.db-wal]==0} {error "Failed to create WAL file!"}
2088f6097c2Sdan
2098f6097c2Sdan  sqlite3 db test.db -vfs shmfault
2108f6097c2Sdan} -sqlbody {
2118f6097c2Sdan  SELECT count(*) FROM t1;
2128f6097c2Sdan}
2138f6097c2Sdan
214a861469aSdando_shmfault_test walfault-shm-4 -tclprep {
215a861469aSdan  sqlite3 db test.db -vfs shmfault
216a861469aSdan  unset -nocomplain ::shmfault_ioerr_countdown
217a861469aSdan  db eval {
218a861469aSdan    PRAGMA page_size = 512;
219a861469aSdan    PRAGMA journal_mode = WAL;
220a861469aSdan    PRAGMA wal_autocheckpoint = 0;
221a861469aSdan    CREATE TABLE t1(x);
222a861469aSdan    BEGIN;
223a861469aSdan      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
224a861469aSdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
225a861469aSdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
226a861469aSdan    COMMIT;
227a861469aSdan  }
228a861469aSdan
229a861469aSdan  set ::shmfault_ioerr_countdown 1
230a861469aSdan  set ::shmfault_ioerr_methods(xShmGet) 1
231a861469aSdan  db close
232a861469aSdan  unset ::shmfault_ioerr_methods(xShmGet)
233a861469aSdan  if {[file exists test.db-wal]==0} {error "Failed to create WAL file!"}
234a861469aSdan  sqlite3 db test.db -vfs shmfault
235a861469aSdan} -sqlbody {
236a861469aSdan  SELECT count(*) FROM t1;
237a861469aSdan}
238a861469aSdan
239d41a29a6Sdando_shmfault_test walfault-shm-5 -coverageonly 1 -sqlprep {
240d41a29a6Sdan  PRAGMA cache_size = 10;
241d41a29a6Sdan  PRAGMA journal_mode = WAL;
242d41a29a6Sdan  CREATE TABLE abc(a PRIMARY KEY);
243d41a29a6Sdan  INSERT INTO abc VALUES(randomblob(900));
244d41a29a6Sdan} -sqlbody {
245d41a29a6Sdan  BEGIN;
246d41a29a6Sdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
247d41a29a6Sdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
248d41a29a6Sdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
249d41a29a6Sdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
250d41a29a6Sdan  ROLLBACK;
251d41a29a6Sdan}
252d41a29a6Sdan
253*65f2ac58Sdan#-------------------------------------------------------------------------
254*65f2ac58Sdan# When a database is checkpointed, SQLite does the following:
255*65f2ac58Sdan#
256*65f2ac58Sdan#   1. xShmLock(CHECKPOINT) to lock the WAL.
257*65f2ac58Sdan#   2. xShmGet(-1) to get a mapping to read the wal-index header.
258*65f2ac58Sdan#   3. If the mapping obtained in (2) is not large enough to cover the
259*65f2ac58Sdan#      entire wal-index, call xShmGet(nReq) to get a larger mapping.
260*65f2ac58Sdan#   4. Do the checkpoint.
261*65f2ac58Sdan#   5. Release the lock and mapping.
262*65f2ac58Sdan#
263*65f2ac58Sdan# This test case tests the outcome of an IO error in step 2.
264*65f2ac58Sdan#
265*65f2ac58Sdanproc shmfault_vfs_cb_6 {method args} {
266*65f2ac58Sdan  switch -- $::shm_state {
267*65f2ac58Sdan    0 { return SQLITE_OK }
268*65f2ac58Sdan    1 {
269*65f2ac58Sdan      if {$method == "xShmGet"} {
270*65f2ac58Sdan        set ::wal_index [tvfs shm [lindex $args 0]]
271*65f2ac58Sdan        tvfs shm [lindex $args 0] [string range $::wal_index 0 65535]
272*65f2ac58Sdan        set ::shm_state 2
273*65f2ac58Sdan      }
274*65f2ac58Sdan    }
275*65f2ac58Sdan    2 {
276*65f2ac58Sdan      if {$method == "xShmGet"} {
277*65f2ac58Sdan        tvfs shm [lindex $args 0] $::wal_index
278*65f2ac58Sdan        return SQLITE_IOERR
279*65f2ac58Sdan      }
280*65f2ac58Sdan    }
281*65f2ac58Sdan  }
282*65f2ac58Sdan  return SQLITE_OK
283*65f2ac58Sdan}
284*65f2ac58Sdando_test walfault-shm-6.1 {
285*65f2ac58Sdan  set ::shm_state 0
286*65f2ac58Sdan  testvfs tvfs shmfault_vfs_cb_6
287*65f2ac58Sdan
288*65f2ac58Sdan  sqlite3 db  test.db -vfs tvfs
289*65f2ac58Sdan  sqlite3 db2 test.db -vfs tvfs
290*65f2ac58Sdan
291*65f2ac58Sdan  execsql {
292*65f2ac58Sdan    PRAGMA journal_mode = WAL;
293*65f2ac58Sdan    PRAGMA wal_autocheckpoint = 0;
294*65f2ac58Sdan    CREATE TABLE t1(x);
295*65f2ac58Sdan    INSERT INTO t1 VALUES(randomblob(900));
296*65f2ac58Sdan  }
297*65f2ac58Sdan} {wal 0}
298*65f2ac58Sdando_test walfault-shm-6.2 {
299*65f2ac58Sdan  execsql {
300*65f2ac58Sdan    PRAGMA wal_autocheckpoint = 0;
301*65f2ac58Sdan    BEGIN;
302*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 2 */
303*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 4 */
304*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 8 */
305*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 16 */
306*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 32 */
307*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 64 */
308*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 128 */
309*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 256 */
310*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 512 */
311*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 1024 */
312*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 2048 */
313*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 4096 */
314*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 8192 */
315*65f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 16384 */
316*65f2ac58Sdan    COMMIT;
317*65f2ac58Sdan  } db2
318*65f2ac58Sdan} {0}
319*65f2ac58Sdando_test walfault-shm-6.3 {
320*65f2ac58Sdan  set ::shm_state 1
321*65f2ac58Sdan  catchsql { PRAGMA wal_checkpoint } db2
322*65f2ac58Sdan} {1 {disk I/O error}}
323*65f2ac58Sdanset ::shm_state 0
324*65f2ac58Sdandb close
325*65f2ac58Sdandb2 close
326*65f2ac58Sdantvfs delete
327*65f2ac58Sdanunset -nocomplain ::wal_index ::shm_state
328*65f2ac58Sdan
32976ed3bc0Sdanfinish_test
330c7991bdfSdan
331