xref: /sqlite-3.40.0/test/walfault.test (revision 8521abfd)
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
22*8521abfdSdando_faultsim_test walfault-oom-1 -prep {
23*8521abfdSdan  catch { db close }
24*8521abfdSdan  file delete -force test.db test.db-wal test.db-journal
25*8521abfdSdan  sqlite3 db test.db
26*8521abfdSdan} -body {
27*8521abfdSdan  db eval { PRAGMA main.journal_mode = WAL }
28*8521abfdSdan} -test {
29*8521abfdSdan
30*8521abfdSdan  faultsim_test_result {0 wal}
31*8521abfdSdan
32*8521abfdSdan  # Test that the connection that encountered an error as part of
33*8521abfdSdan  # "PRAGMA journal_mode = WAL" and a new connection use the same
34*8521abfdSdan  # journal mode when accessing the database.
35*8521abfdSdan  #
36*8521abfdSdan  # If "PRAGMA journal_mode" is executed immediately, connection [db] (the
37*8521abfdSdan  # one that hit the error in journal_mode="WAL") might return "wal" even
38*8521abfdSdan  # if it failed to switch the database to WAL mode. This is not considered
39*8521abfdSdan  # a problem. When it tries to read the database, connection [db] correctly
40*8521abfdSdan  # recognizes that it is a rollback database and switches back to a
41*8521abfdSdan  # rollback compatible journal mode.
42*8521abfdSdan  #
43*8521abfdSdan  set jm  [db one  {SELECT * FROM sqlite_master ; PRAGMA main.journal_mode}]
44*8521abfdSdan  sqlite3 db2 test.db
45*8521abfdSdan  set jm2 [db2 one {SELECT * FROM sqlite_master ; PRAGMA main.journal_mode}]
46*8521abfdSdan  db2 close
47*8521abfdSdan
48*8521abfdSdan  if { $jm!=$jm2 } { error "Journal modes do not match: $jm $jm2" }
49*8521abfdSdan  if { $testrc==0 && $jm!="wal" } { error "Journal mode is not WAL" }
5076ed3bc0Sdan}
5176ed3bc0Sdan
525e9e482fSdando_malloc_test walfault-oom-2 -tclprep {
535e9e482fSdan  execsql {
545e9e482fSdan    PRAGMA journal_mode = WAL;
555e9e482fSdan    BEGIN;
565e9e482fSdan      CREATE TABLE x(y, z, UNIQUE(y, z));
575e9e482fSdan      INSERT INTO x VALUES(randomblob(100), randomblob(100));
585e9e482fSdan    COMMIT;
595e9e482fSdan    PRAGMA wal_checkpoint;
605e9e482fSdan
615e9e482fSdan    INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
625e9e482fSdan    INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
635e9e482fSdan    INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
645e9e482fSdan  }
655e9e482fSdan  file copy -force test.db testX.db
665e9e482fSdan  file copy -force test.db-wal testX.db-wal
675e9e482fSdan  db close
685e9e482fSdan  file rename -force testX.db test.db
695e9e482fSdan  file rename -force testX.db-wal test.db-wal
705e9e482fSdan
715e9e482fSdan  sqlite3 db test.db
725e9e482fSdan  sqlite3_extended_result_codes db 1
735e9e482fSdan  sqlite3_db_config_lookaside db 0 0 0
745e9e482fSdan} -sqlbody {
755e9e482fSdan  SELECT count(*) FROM x;
765e9e482fSdan}
775e9e482fSdan
789a6b4e9aSdando_ioerr_test walfault-ioerr-1 -sqlprep {
799a6b4e9aSdan  PRAGMA auto_vacuum = 1;
809a6b4e9aSdan  PRAGMA journal_mode = WAL;
819a6b4e9aSdan  CREATE TABLE abc(a PRIMARY KEY);
829a6b4e9aSdan  INSERT INTO abc VALUES(randomblob(1500));
839a6b4e9aSdan} -sqlbody {
849a6b4e9aSdan  DELETE FROM abc;
859a6b4e9aSdan  PRAGMA wal_checkpoint;
869a6b4e9aSdan}
87a10069ddSshanehcatch {db close}
889a6b4e9aSdan
89c7991bdfSdan# A [testvfs] callback for the VFS created by [do_shmfault_test]. This
908f6097c2Sdan# callback injects SQLITE_IOERR faults into methods for which an entry
918f6097c2Sdan# in array ::shmfault_ioerr_methods is defined. For example, to enable
928f6097c2Sdan# errors in xShmOpen:
93c7991bdfSdan#
948f6097c2Sdan#   set ::shmfault_ioerr_methods(xShmOpen) 1
95c7991bdfSdan#
96c7991bdfSdan# Faults are not injected into xShmRelease, xShmClose or xShmLock method
97c7991bdfSdan# calls. The global tcl variables used are:
98c7991bdfSdan#
99c7991bdfSdan#   $::shmfault_ioerr_countdown
100c7991bdfSdan#   $::shmfault_ioerr_persist
1018f6097c2Sdan#   $::shmfault_ioerr_methods
102c7991bdfSdan#
103c7991bdfSdanproc shmfault_vfs_cb {method args} {
104c7991bdfSdan
105c7991bdfSdan  # If ::shmfault_ioerr_countdown is not set, always return SQLITE_OK.
106c7991bdfSdan  #
107c7991bdfSdan  if {[info exists ::shmfault_ioerr_countdown]==0} { return SQLITE_OK }
108c7991bdfSdan
1098f6097c2Sdan  if {[info exists ::shmfault_ioerr_methods($method)]} {
110c7991bdfSdan    incr ::shmfault_ioerr_countdown -1
111c7991bdfSdan    if { ($::shmfault_ioerr_countdown==0)
112c7991bdfSdan      || ($::shmfault_ioerr_countdown<=0 && $::shmfault_ioerr_persist)
113c7991bdfSdan    } {
114c7991bdfSdan      return SQLITE_IOERR
115c7991bdfSdan    }
116c7991bdfSdan  }
117c7991bdfSdan  return SQLITE_OK
118c7991bdfSdan}
119c7991bdfSdan
1208f6097c2Sdan# Options are:
1218f6097c2Sdan#
1228f6097c2Sdan#   -tclprep TCL
1238f6097c2Sdan#   -sqlprep SQL
1248f6097c2Sdan#   -sqlbody SQL
1258f6097c2Sdan#
126c7991bdfSdanproc do_shmfault_test {name args} {
1278f6097c2Sdan
1288f6097c2Sdan  set A(-tclprep) "sqlite3 db test.db -vfs shmfault"
1298f6097c2Sdan  set A(-sqlprep) ""
1308f6097c2Sdan  set A(-sqlbody) ""
1318f6097c2Sdan  set A(-methods) [list xShmGet xShmOpen xShmSize]
132d41a29a6Sdan  set A(-coverageonly) 0
133c7991bdfSdan  array set A $args
134c7991bdfSdan
135c7991bdfSdan  # Create a VFS to use:
136c7991bdfSdan  testvfs shmfault shmfault_vfs_cb
137c7991bdfSdan
1388f6097c2Sdan  unset -nocomplain ::shmfault_ioerr_methods
1398f6097c2Sdan  foreach m $A(-methods) { set ::shmfault_ioerr_methods($m) 1 }
1408f6097c2Sdan
141c7991bdfSdan  foreach mode {transient persistent} {
142c7991bdfSdan    set ::shmfault_ioerr_persist [expr {$mode == "persistent"}]
1438f6097c2Sdan
144c7991bdfSdan    for {set nDelay 1} {$nDelay < 10000} {incr nDelay} {
145c7991bdfSdan
146c7991bdfSdan      file delete -force test.db test.db-wal test.db-journal
147c7991bdfSdan
1488f6097c2Sdan      eval $A(-tclprep)
1498f6097c2Sdan      db eval $A(-sqlprep)
1508f6097c2Sdan
1518f6097c2Sdan      set ::shmfault_ioerr_countdown $nDelay
1528f6097c2Sdan      set rc [catch { db eval $A(-sqlbody) } msg]
153c7991bdfSdan      set hit_error [expr {$::shmfault_ioerr_countdown<=0}]
154c7991bdfSdan      unset ::shmfault_ioerr_countdown
1558f6097c2Sdan
156c7991bdfSdan      catch { db close }
157c7991bdfSdan
158d41a29a6Sdan      if {$A(-coverageonly)} { set rc $hit_error }
159c7991bdfSdan      do_test $name-$mode.$nDelay.1 [list set {} $hit_error] $rc
160c7991bdfSdan
161c7991bdfSdan      if {$hit_error==0} break
162c7991bdfSdan    }
163c7991bdfSdan  }
164c7991bdfSdan
165c7991bdfSdan  shmfault delete
166c7991bdfSdan}
167c7991bdfSdan
168c7991bdfSdando_shmfault_test walfault-shm-1 -sqlbody {
169c7991bdfSdan  PRAGMA journal_mode = WAL;
170c7991bdfSdan  CREATE TABLE t1(a PRIMARY KEY, b);
171c7991bdfSdan  INSERT INTO t1 VALUES('a', 'b');
172c7991bdfSdan  PRAGMA wal_checkpoint;
173c7991bdfSdan}
17476ed3bc0Sdan
1758f6097c2Sdando_shmfault_test walfault-shm-2 -methods xShmSize -sqlprep {
1768f6097c2Sdan  PRAGMA page_size = 512;
1778f6097c2Sdan  PRAGMA journal_mode = WAL;
1788f6097c2Sdan  PRAGMA wal_autocheckpoint = 0;
1798f6097c2Sdan} -sqlbody {
1808f6097c2Sdan  CREATE TABLE t1(x);
1818f6097c2Sdan  BEGIN;
1828f6097c2Sdan    INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
1838f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
1848f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
1858f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
1868f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
1878f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
1888f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
1898f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
1908f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
1918f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
1928f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
1938f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
1948f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
1958f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
1968f6097c2Sdan    INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
1978f6097c2Sdan  COMMIT;
1988f6097c2Sdan}
1998f6097c2Sdan
2008f6097c2Sdando_shmfault_test walfault-shm-3 -methods xShmSize -tclprep {
2018f6097c2Sdan  sqlite3 db test.db -vfs shmfault
2028f6097c2Sdan  unset -nocomplain ::shmfault_ioerr_countdown
2038f6097c2Sdan  db eval {
2048f6097c2Sdan    PRAGMA page_size = 512;
2058f6097c2Sdan    PRAGMA journal_mode = WAL;
2068f6097c2Sdan    PRAGMA wal_autocheckpoint = 0;
2078f6097c2Sdan    CREATE TABLE t1(x);
2088f6097c2Sdan    BEGIN;
2098f6097c2Sdan      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
2108f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
2118f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
2128f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8 */
2138f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16 */
2148f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 32 */
2158f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 64 */
2168f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 128 */
2178f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 256 */
2188f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 512 */
2198f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 1024 */
2208f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2048 */
2218f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4096 */
2228f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 8192 */
2238f6097c2Sdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
2248f6097c2Sdan    COMMIT;
2258f6097c2Sdan  }
2268f6097c2Sdan
2278f6097c2Sdan  set ::shmfault_ioerr_countdown 1
2288f6097c2Sdan  set ::shmfault_ioerr_methods(xShmGet) 1
2298f6097c2Sdan  db close
2308f6097c2Sdan  unset ::shmfault_ioerr_methods(xShmGet)
2318f6097c2Sdan  if {[file exists test.db-wal]==0} {error "Failed to create WAL file!"}
2328f6097c2Sdan
2338f6097c2Sdan  sqlite3 db test.db -vfs shmfault
2348f6097c2Sdan} -sqlbody {
2358f6097c2Sdan  SELECT count(*) FROM t1;
2368f6097c2Sdan}
2378f6097c2Sdan
238a861469aSdando_shmfault_test walfault-shm-4 -tclprep {
239a861469aSdan  sqlite3 db test.db -vfs shmfault
240a861469aSdan  unset -nocomplain ::shmfault_ioerr_countdown
241a861469aSdan  db eval {
242a861469aSdan    PRAGMA page_size = 512;
243a861469aSdan    PRAGMA journal_mode = WAL;
244a861469aSdan    PRAGMA wal_autocheckpoint = 0;
245a861469aSdan    CREATE TABLE t1(x);
246a861469aSdan    BEGIN;
247a861469aSdan      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
248a861469aSdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
249a861469aSdan      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
250a861469aSdan    COMMIT;
251a861469aSdan  }
252a861469aSdan
253a861469aSdan  set ::shmfault_ioerr_countdown 1
254a861469aSdan  set ::shmfault_ioerr_methods(xShmGet) 1
255a861469aSdan  db close
256a861469aSdan  unset ::shmfault_ioerr_methods(xShmGet)
257a861469aSdan  if {[file exists test.db-wal]==0} {error "Failed to create WAL file!"}
258a861469aSdan  sqlite3 db test.db -vfs shmfault
259a861469aSdan} -sqlbody {
260a861469aSdan  SELECT count(*) FROM t1;
261a861469aSdan}
262a861469aSdan
2638067adbcSdando_shmfault_test walfault-shm-5.1 -coverageonly 1 -sqlprep {
264d41a29a6Sdan  PRAGMA cache_size = 10;
265d41a29a6Sdan  PRAGMA journal_mode = WAL;
266d41a29a6Sdan  CREATE TABLE abc(a PRIMARY KEY);
267d41a29a6Sdan  INSERT INTO abc VALUES(randomblob(900));
268d41a29a6Sdan} -sqlbody {
269d41a29a6Sdan  BEGIN;
270d41a29a6Sdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
271d41a29a6Sdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
272d41a29a6Sdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
273d41a29a6Sdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
274d41a29a6Sdan  ROLLBACK;
275d41a29a6Sdan}
276d41a29a6Sdan
2778067adbcSdando_shmfault_test walfault-shm-5.2 -coverageonly 1 -sqlprep {
2788067adbcSdan  PRAGMA cache_size = 10;
2798067adbcSdan  PRAGMA journal_mode = WAL;
2808067adbcSdan  CREATE TABLE abc(a PRIMARY KEY);
2818067adbcSdan  INSERT INTO abc VALUES(randomblob(900));
2828067adbcSdan} -sqlbody {
2838067adbcSdan  BEGIN;
2848067adbcSdan    INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 1 */
2858067adbcSdan    SAVEPOINT spoint;
2868067adbcSdan      INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 2 */
2878067adbcSdan      INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 4 */
2888067adbcSdan      INSERT INTO abc SELECT randomblob(900) FROM abc;    /* 8 */
2898067adbcSdan    ROLLBACK TO spoint;
2908067adbcSdan  COMMIT;
2918067adbcSdan}
2928067adbcSdan
29365f2ac58Sdan#-------------------------------------------------------------------------
29465f2ac58Sdan# When a database is checkpointed, SQLite does the following:
29565f2ac58Sdan#
29665f2ac58Sdan#   1. xShmLock(CHECKPOINT) to lock the WAL.
29765f2ac58Sdan#   2. xShmGet(-1) to get a mapping to read the wal-index header.
29865f2ac58Sdan#   3. If the mapping obtained in (2) is not large enough to cover the
29965f2ac58Sdan#      entire wal-index, call xShmGet(nReq) to get a larger mapping.
30065f2ac58Sdan#   4. Do the checkpoint.
30165f2ac58Sdan#   5. Release the lock and mapping.
30265f2ac58Sdan#
30365f2ac58Sdan# This test case tests the outcome of an IO error in step 2.
30465f2ac58Sdan#
30565f2ac58Sdanproc shmfault_vfs_cb_6 {method args} {
30665f2ac58Sdan  switch -- $::shm_state {
30765f2ac58Sdan    0 { return SQLITE_OK }
30865f2ac58Sdan    1 {
30965f2ac58Sdan      if {$method == "xShmGet"} {
31065f2ac58Sdan        set ::wal_index [tvfs shm [lindex $args 0]]
31165f2ac58Sdan        tvfs shm [lindex $args 0] [string range $::wal_index 0 65535]
31265f2ac58Sdan        set ::shm_state 2
31365f2ac58Sdan      }
31465f2ac58Sdan    }
31565f2ac58Sdan    2 {
31665f2ac58Sdan      if {$method == "xShmGet"} {
31765f2ac58Sdan        tvfs shm [lindex $args 0] $::wal_index
31865f2ac58Sdan        return SQLITE_IOERR
31965f2ac58Sdan      }
32065f2ac58Sdan    }
32165f2ac58Sdan  }
32265f2ac58Sdan  return SQLITE_OK
32365f2ac58Sdan}
32465f2ac58Sdando_test walfault-shm-6.1 {
32565f2ac58Sdan  set ::shm_state 0
32665f2ac58Sdan  testvfs tvfs shmfault_vfs_cb_6
32765f2ac58Sdan
32865f2ac58Sdan  sqlite3 db  test.db -vfs tvfs
32965f2ac58Sdan  sqlite3 db2 test.db -vfs tvfs
33065f2ac58Sdan
33165f2ac58Sdan  execsql {
33265f2ac58Sdan    PRAGMA journal_mode = WAL;
33365f2ac58Sdan    PRAGMA wal_autocheckpoint = 0;
33465f2ac58Sdan    CREATE TABLE t1(x);
33565f2ac58Sdan    INSERT INTO t1 VALUES(randomblob(900));
33665f2ac58Sdan  }
33765f2ac58Sdan} {wal 0}
33865f2ac58Sdando_test walfault-shm-6.2 {
33965f2ac58Sdan  execsql {
34065f2ac58Sdan    PRAGMA wal_autocheckpoint = 0;
34165f2ac58Sdan    BEGIN;
34265f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 2 */
34365f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 4 */
34465f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 8 */
34565f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 16 */
34665f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 32 */
34765f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 64 */
34865f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 128 */
34965f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 256 */
35065f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 512 */
35165f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 1024 */
35265f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 2048 */
35365f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 4096 */
35465f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 8192 */
35565f2ac58Sdan      INSERT INTO t1 SELECT randomblob(900) FROM t1;    /* 16384 */
35665f2ac58Sdan    COMMIT;
35765f2ac58Sdan  } db2
35865f2ac58Sdan} {0}
35965f2ac58Sdando_test walfault-shm-6.3 {
36065f2ac58Sdan  set ::shm_state 1
36165f2ac58Sdan  catchsql { PRAGMA wal_checkpoint } db2
36265f2ac58Sdan} {1 {disk I/O error}}
36365f2ac58Sdanset ::shm_state 0
36465f2ac58Sdandb close
36565f2ac58Sdandb2 close
36665f2ac58Sdantvfs delete
36765f2ac58Sdanunset -nocomplain ::wal_index ::shm_state
36865f2ac58Sdan
36976ed3bc0Sdanfinish_test
370c7991bdfSdan
371