xref: /sqlite-3.40.0/test/journal2.test (revision 05accd22)
12a321c75Sdan# 2010 June 16
22a321c75Sdan#
32a321c75Sdan# The author disclaims copyright to this source code.  In place of
42a321c75Sdan# a legal notice, here is a blessing:
52a321c75Sdan#
62a321c75Sdan#    May you do good and not evil.
72a321c75Sdan#    May you find forgiveness for yourself and forgive others.
82a321c75Sdan#    May you share freely, never taking more than you give.
92a321c75Sdan#
102a321c75Sdan#***********************************************************************
11c74e4ef4Sdan# This file implements regression tests for SQLite library. Specifically,
12c74e4ef4Sdan# it tests SQLite when using a VFS that claims the SAFE_DELETE property.
132a321c75Sdan#
142a321c75Sdan
152a321c75Sdanset testdir [file dirname $argv0]
162a321c75Sdansource $testdir/tester.tcl
172a321c75Sdansource $testdir/lock_common.tcl
182a321c75Sdansource $testdir/malloc_common.tcl
192a321c75Sdandb close
202a321c75Sdan
21b3f4351fSdanif {[permutation] == "inmemory_journal"} {
22b3f4351fSdan  finish_test
23b3f4351fSdan  return
24b3f4351fSdan}
25b3f4351fSdan
262a321c75Sdanset a_string_counter 1
272a321c75Sdanproc a_string {n} {
282a321c75Sdan  global a_string_counter
292a321c75Sdan  incr a_string_counter
302a321c75Sdan  string range [string repeat "${a_string_counter}." $n] 1 $n
312a321c75Sdan}
322a321c75Sdan
332a321c75Sdan# Create a [testvfs] and install it as the default VFS. Set the device
342a321c75Sdan# characteristics flags to "SAFE_DELETE".
352a321c75Sdan#
362a321c75Sdantestvfs tvfs -default 1
37cb15f35fSdrhtvfs devchar {undeletable_when_open powersafe_overwrite}
382a321c75Sdan
392a321c75Sdan# Set up a hook so that each time a journal file is opened, closed or
402a321c75Sdan# deleted, the method name ("xOpen", "xClose" or "xDelete") and the final
412a321c75Sdan# segment of the journal file-name (i.e. "test.db-journal") are appended to
422a321c75Sdan# global list variable $::oplog.
432a321c75Sdan#
442a321c75Sdantvfs filter {xOpen xClose xDelete}
452a321c75Sdantvfs script journal_op_catcher
462a321c75Sdanproc journal_op_catcher {method filename args} {
472a321c75Sdan
482a321c75Sdan  # If global variable ::tvfs_error_on_write is defined, then return an
492a321c75Sdan  # IO error to every attempt to modify the file-system. Otherwise, return
502a321c75Sdan  # SQLITE_OK.
512a321c75Sdan  #
522a321c75Sdan  if {[info exists ::tvfs_error_on_write]} {
5324827d0cSdan    if {[lsearch {xDelete xWrite xTruncate} $method]>=0} {
542a321c75Sdan      return SQLITE_IOERR
552a321c75Sdan    }
562a321c75Sdan  }
572a321c75Sdan
5824827d0cSdan  # The rest of this command only deals with xOpen(), xClose() and xDelete()
5924827d0cSdan  # operations on journal files. If this invocation does not represent such
6024827d0cSdan  # an operation, return with no further ado.
6124827d0cSdan  #
622a321c75Sdan  set f [file tail $filename]
6324827d0cSdan  if {[string match *journal $f]==0} return
6424827d0cSdan  if {[lsearch {xOpen xDelete xClose} $method]<0} return
6524827d0cSdan
6624827d0cSdan  # Append a record of this operation to global list variable $::oplog.
6724827d0cSdan  #
682a321c75Sdan  lappend ::oplog $method $f
692a321c75Sdan
7024827d0cSdan  # If this is an attempt to delete a journal file for which there exists
7124827d0cSdan  # one ore more open handles, return an error. The code in test_vfs.c
7224827d0cSdan  # will not invoke the xDelete method of the "real" VFS in this case.
7324827d0cSdan  #
742a321c75Sdan  if {[info exists ::open_journals($f)]==0} { set ::open_journals($f) 0 }
752a321c75Sdan  switch -- $method {
7624827d0cSdan    xOpen   { incr ::open_journals($f) +1 }
7724827d0cSdan    xClose  { incr ::open_journals($f) -1 }
78b70f82a7Sdan    xDelete { if {$::open_journals($f)>0} { return SQLITE_IOERR } }
792a321c75Sdan  }
802a321c75Sdan
8124827d0cSdan  return ""
822a321c75Sdan}
832a321c75Sdan
842a321c75Sdan
852a321c75Sdando_test journal2-1.1 {
862a321c75Sdan  set ::oplog [list]
872a321c75Sdan  sqlite3 db test.db
882a321c75Sdan  execsql { CREATE TABLE t1(a, b) }
892a321c75Sdan  set ::oplog
902a321c75Sdan} {xOpen test.db-journal xClose test.db-journal xDelete test.db-journal}
912a321c75Sdando_test journal2-1.2 {
922a321c75Sdan  set ::oplog [list]
932a321c75Sdan  execsql {
942a321c75Sdan    PRAGMA journal_mode = truncate;
952a321c75Sdan    INSERT INTO t1 VALUES(1, 2);
962a321c75Sdan  }
972a321c75Sdan  set ::oplog
982a321c75Sdan} {xOpen test.db-journal}
992a321c75Sdando_test journal2-1.3 {
1002a321c75Sdan  set ::oplog [list]
1012a321c75Sdan  execsql { INSERT INTO t1 VALUES(3, 4) }
1022a321c75Sdan  set ::oplog
1032a321c75Sdan} {}
1042a321c75Sdando_test journal2-1.4 { execsql { SELECT * FROM t1 } } {1 2 3 4}
1052a321c75Sdan
1062a321c75Sdan# Add a second connection. This connection attempts to commit data in
1072a321c75Sdan# journal_mode=DELETE mode. When it tries to delete the journal file,
1082a321c75Sdan# the VFS layer returns an IO error.
1092a321c75Sdan#
1102a321c75Sdando_test journal2-1.5 {
1112a321c75Sdan  set ::oplog [list]
1122a321c75Sdan  sqlite3 db2 test.db
1132a321c75Sdan  execsql  { PRAGMA journal_mode = delete } db2
1142a321c75Sdan  catchsql { INSERT INTO t1 VALUES(5, 6)  } db2
1152a321c75Sdan} {1 {disk I/O error}}
1162a321c75Sdando_test journal2-1.6 { file exists test.db-journal } 1
1172a321c75Sdando_test journal2-1.7 { execsql { SELECT * FROM t1 } } {1 2 3 4}
1182a321c75Sdando_test journal2-1.8 {
1192a321c75Sdan  execsql { PRAGMA journal_mode = truncate } db2
1202a321c75Sdan  execsql { INSERT INTO t1 VALUES(5, 6)  } db2
1212a321c75Sdan} {}
1222a321c75Sdando_test journal2-1.9 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6}
1232a321c75Sdan
12424827d0cSdan# Grow the database until it is reasonably large.
1252a321c75Sdan#
1262a321c75Sdando_test journal2-1.10 {
1272a321c75Sdan  db2 close
1282a321c75Sdan  db func a_string a_string
1292a321c75Sdan  execsql {
1302a321c75Sdan    CREATE TABLE t2(a UNIQUE, b UNIQUE);
1312a321c75Sdan    INSERT INTO t2 VALUES(a_string(200), a_string(300));
1322a321c75Sdan    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  --  2
1332a321c75Sdan    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  --  4
1342a321c75Sdan    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  --  8
1352a321c75Sdan    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  -- 16
1362a321c75Sdan    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  -- 32
1372a321c75Sdan    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  -- 64
1382a321c75Sdan  }
1392a321c75Sdan  file size test.db-journal
1402a321c75Sdan} {0}
1412a321c75Sdando_test journal2-1.11 {
1422a321c75Sdan  set sz [expr [file size test.db] / 1024]
1432a321c75Sdan  expr {$sz>120 && $sz<200}
1442a321c75Sdan} 1
1452a321c75Sdan
14624827d0cSdan# Using new connection [db2] (with journal_mode=DELETE), write a lot of
14724827d0cSdan# data to the database. So that many pages within the database file are
14824827d0cSdan# modified before the transaction is committed.
14924827d0cSdan#
15024827d0cSdan# Then, enable simulated IO errors in all calls to xDelete, xWrite
15124827d0cSdan# and xTruncate before committing the transaction and closing the
15224827d0cSdan# database file. From the point of view of other file-system users, it
15324827d0cSdan# appears as if the process hosting [db2] unexpectedly exited.
15424827d0cSdan#
1552a321c75Sdando_test journal2-1.12 {
1562a321c75Sdan  sqlite3 db2 test.db
1572a321c75Sdan  execsql {
1582a321c75Sdan    PRAGMA cache_size = 10;
1592a321c75Sdan    BEGIN;
1602a321c75Sdan      INSERT INTO t2 SELECT randomblob(200), randomblob(300) FROM t2;  -- 128
1612a321c75Sdan  } db2
1622a321c75Sdan} {}
1632a321c75Sdando_test journal2-1.13 {
1642a321c75Sdan  tvfs filter {xOpen xClose xDelete xWrite xTruncate}
1652a321c75Sdan  set ::tvfs_error_on_write 1
1662a321c75Sdan  catchsql { COMMIT } db2
1672a321c75Sdan} {1 {disk I/O error}}
1682a321c75Sdandb2 close
1692a321c75Sdanunset ::tvfs_error_on_write
170fda06befSmistachkinforcecopy test.db testX.db
1712a321c75Sdan
1722a321c75Sdando_test journal2-1.14 { file exists test.db-journal } 1
1732a321c75Sdando_test journal2-1.15 {
1742a321c75Sdan  execsql {
1752a321c75Sdan    SELECT count(*) FROM t2;
1762a321c75Sdan    PRAGMA integrity_check;
1772a321c75Sdan  }
1782a321c75Sdan} {64 ok}
1792a321c75Sdan
1802a321c75Sdan# This block checks that in the test case above, connection [db2] really
1812a321c75Sdan# did begin writing to the database file before it hit IO errors. If
1822a321c75Sdan# this is true, then the copy of the database file made before [db]
1832a321c75Sdan# rolled back the hot journal should fail the integrity-check.
1842a321c75Sdan#
1852a321c75Sdando_test journal2-1.16 {
1862a321c75Sdan  set sz [expr [file size testX.db] / 1024]
1872a321c75Sdan  expr {$sz>240 && $sz<400}
1882a321c75Sdan} 1
1892a321c75Sdando_test journal2-1.17 {
1902a321c75Sdan  expr {[catchsql { PRAGMA integrity_check } db] == "0 ok"}
1912a321c75Sdan} {1}
1922a321c75Sdando_test journal2-1.20 {
1932a321c75Sdan  sqlite3 db2 testX.db
1942a321c75Sdan  expr {[catchsql { PRAGMA integrity_check } db2] == "0 ok"}
1952a321c75Sdan} {0}
1962a321c75Sdando_test journal2-1.21 {
1972a321c75Sdan  db2 close
1982a321c75Sdan} {}
1992a321c75Sdandb close
20024827d0cSdan
20124827d0cSdan#-------------------------------------------------------------------------
20224827d0cSdan# Test that it is possible to switch from journal_mode=truncate to
20324827d0cSdan# journal_mode=WAL on a SAFE_DELETE file-system. SQLite should close and
20424827d0cSdan# delete the journal file when committing the transaction that switches
20524827d0cSdan# the system to WAL mode.
20624827d0cSdan#
207*05accd22Sdanif {[wal_is_capable]} {
20824827d0cSdan  do_test journal2-2.1 {
20924827d0cSdan    faultsim_delete_and_reopen
21024827d0cSdan    set ::oplog [list]
21124827d0cSdan    execsql { PRAGMA journal_mode = persist }
21224827d0cSdan    set ::oplog
21324827d0cSdan  } {}
21424827d0cSdan  do_test journal2-2.2 {
21524827d0cSdan    execsql {
21624827d0cSdan      CREATE TABLE t1(x);
21724827d0cSdan      INSERT INTO t1 VALUES(3.14159);
21824827d0cSdan    }
21924827d0cSdan    set ::oplog
22024827d0cSdan  } {xOpen test.db-journal}
22124827d0cSdan  do_test journal2-2.3 {
22224827d0cSdan    expr {[file size test.db-journal] > 512}
22324827d0cSdan  } {1}
224731bf5bcSdan  do_test journal2-2.4 {
22524827d0cSdan    set ::oplog [list]
22624827d0cSdan    execsql { PRAGMA journal_mode = WAL }
22724827d0cSdan    set ::oplog
228731bf5bcSdan  } {xClose test.db-journal xDelete test.db-journal}
22924827d0cSdan  db close
23024827d0cSdan}
23124827d0cSdan
2322a321c75Sdantvfs delete
2332a321c75Sdanfinish_test
234