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