Index: src/test_vfs.c ================================================================== --- src/test_vfs.c +++ src/test_vfs.c @@ -121,10 +121,12 @@ #define TESTVFS_CLOSE_MASK 0x00000800 #define TESTVFS_WRITE_MASK 0x00001000 #define TESTVFS_TRUNCATE_MASK 0x00002000 #define TESTVFS_ACCESS_MASK 0x00004000 #define TESTVFS_FULLPATHNAME_MASK 0x00008000 +#define TESTVFS_READ_MASK 0x00010000 + #define TESTVFS_ALL_MASK 0x0001FFFF #define TESTVFS_MAX_PAGES 1024 @@ -323,12 +325,26 @@ sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ - TestvfsFd *p = tvfsGetFd(pFile); - return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); + int rc = SQLITE_OK; + TestvfsFd *pFd = tvfsGetFd(pFile); + Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; + if( p->pScript && p->mask&TESTVFS_READ_MASK ){ + tvfsExecTcl(p, "xRead", + Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 + ); + tvfsResultCode(p, &rc); + } + if( rc==SQLITE_OK && p->mask&TESTVFS_READ_MASK && tvfsInjectIoerr(p) ){ + rc = SQLITE_IOERR; + } + if( rc==SQLITE_OK ){ + rc = sqlite3OsRead(pFd->pReal, zBuf, iAmt, iOfst); + } + return rc; } /* ** Write data to an tvfs-file. */ @@ -1028,10 +1044,11 @@ { "xShmUnmap", TESTVFS_SHMCLOSE_MASK }, { "xShmMap", TESTVFS_SHMMAP_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xDelete", TESTVFS_DELETE_MASK }, { "xWrite", TESTVFS_WRITE_MASK }, + { "xRead", TESTVFS_READ_MASK }, { "xTruncate", TESTVFS_TRUNCATE_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, { "xClose", TESTVFS_CLOSE_MASK }, { "xAccess", TESTVFS_ACCESS_MASK }, { "xFullPathname", TESTVFS_FULLPATHNAME_MASK }, Index: test/indexfault.test ================================================================== --- test/indexfault.test +++ test/indexfault.test @@ -14,10 +14,48 @@ source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl set testprefix indexfault + +# Set up the custom fault-injector. This is further configured by using +# different values for $::custom_filter and different implementations +# of Tcl proc [xCustom] for each test case. +# +proc install_custom_faultsim {} { + set ::FAULTSIM(custom) [list \ + -injectinstall custom_injectinstall \ + -injectstart custom_injectstart \ + -injectstop custom_injectstop \ + -injecterrlist {{1 {disk I/O error}}} \ + -injectuninstall custom_injectuninstall \ + ] + proc custom_injectinstall {} { + testvfs shmfault -default true + shmfault filter $::custom_filter + shmfault script xCustom + } + proc custom_injectuninstall {} { + catch {db close} + catch {db2 close} + shmfault delete + } + set ::custom_ifail -1 + set ::custom_nfail -1 + proc custom_injectstart {iFail} { + set ::custom_ifail $iFail + set ::custom_nfail 0 + } + proc custom_injectstop {} { + set ::custom_ifail -1 + return $::custom_nfail + } +} +proc uninstall_custom_faultsim {} { + unset -nocomplain ::FAULTSIM(custom) +} + #------------------------------------------------------------------------- # These tests - indexfault-1.* - Build an index on a smallish table with # all different kinds of fault-injection. The CREATE INDEX is run once # with default options and once with a 50KB soft-heap-limit. @@ -122,43 +160,18 @@ # # 3.3: IO errors injected into the first 200 write() calls made on the # second temporary file. # 3.4: As 7.3, but with a low (50KB) soft-heap-limit. # -# - -# Set up the custom fault-injector. This is further configured by using -# different values for $::custom_filter and different implementations -# of Tcl proc [xCustom] for each test case. -# -set FAULTSIM(custom) [list \ - -injectinstall custom_injectinstall \ - -injectstart custom_injectstart \ - -injectstop custom_injectstop \ - -injecterrlist {{1 {disk I/O error}}} \ - -injectuninstall custom_injectuninstall \ -] -proc custom_injectinstall {} { - testvfs shmfault -default true - shmfault filter $::custom_filter - shmfault script xCustom -} -proc custom_injectuninstall {} { - catch {db close} - catch {db2 close} - shmfault delete -} -set ::custom_ifail -1 -set ::custom_nfail -1 -proc custom_injectstart {iFail} { - set ::custom_ifail $iFail - set ::custom_nfail 0 -} -proc custom_injectstop {} { - set ::custom_ifail -1 - return $::custom_nfail -} +# 3.5: After a certain amount of data has been read from the main database +# file (and written into the temporary b-tree), sqlite3_release_memory() +# is called to free as much memory as possible. This causes the temp +# b-tree to be flushed to disk. So that before its contents can be +# transfered to a PMA they must be read back from disk - creating extra +# opportunities for IO errors. +# +install_custom_faultsim # Set up a table to build indexes on. Save the setup using the # [faultsim_save_and_close] mechanism. # sqlite3 db test.db @@ -242,7 +255,83 @@ execsql { CREATE INDEX i1 ON t1(x) } faultsim_test_result {0 {}} } sqlite3_soft_heap_limit $soft_limit } + +uninstall_custom_faultsim + +#------------------------------------------------------------------------- +# Test 4: After a certain amount of data has been read from the main database +# file (and written into the temporary b-tree), sqlite3_release_memory() is +# called to free as much memory as possible. This causes the temp b-tree to be +# flushed to disk. So that before its contents can be transfered to a PMA they +# must be read back from disk - creating extra opportunities for IO errors. +# +install_custom_faultsim + +catch { db close } +forcedelete test.db +sqlite3 db test.db + +do_execsql_test 4.0 { + BEGIN; + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(randomblob(11000)); + INSERT INTO t1 SELECT randomblob(11001) FROM t1; -- 2 + INSERT INTO t1 SELECT randomblob(11002) FROM t1; -- 4 + INSERT INTO t1 SELECT randomblob(11003) FROM t1; -- 8 + INSERT INTO t1 SELECT randomblob(11004) FROM t1; -- 16 + INSERT INTO t1 SELECT randomblob(11005) FROM t1; -- 32 + INSERT INTO t1 SELECT randomblob(11005) FROM t1; -- 64 + COMMIT; +} +faultsim_save_and_close + +testvfs tvfs +tvfs script xRead +tvfs filter xRead +set ::nRead 0 +proc xRead {method file args} { + if {[file tail $file] == "test.db"} { incr ::nRead } +} + +do_test 4.1 { + sqlite3 db test.db -vfs tvfs + execsql { CREATE INDEX i1 ON t1(x) } +} {} + +db close +tvfs delete + +set ::custom_filter xRead +proc xCustom {method file args} { + incr ::nReadCall + if {$::nReadCall >= ($::nRead/5)} { + if {$::nReadCall == ($::nRead/5)} { + set nByte [sqlite3_release_memory [expr 64*1024*1024]] + sqlite3_soft_heap_limit 20000 + } + if {$file == ""} { + incr ::custom_ifail -1 + if {$::custom_ifail==0} { + incr ::custom_nfail + return "SQLITE_IOERR" + } + } + } + return "SQLITE_OK" +} + +do_faultsim_test 4.2 -faults custom -prep { + faultsim_restore_and_reopen + set ::nReadCall 0 + sqlite3_soft_heap_limit 0 +} -body { + execsql { CREATE INDEX i1 ON t1(x) } + faultsim_test_result {0 {}} +} + +uninstall_custom_faultsim finish_test Index: test/wal2.test ================================================================== --- test/wal2.test +++ test/wal2.test @@ -604,10 +604,11 @@ return "SQLITE_OK" } testvfs tvfs tvfs script tvfs_cb sqlite3 db test.db -vfs tvfs + set {} {} } {} set RECOVERY { {0 1 lock exclusive} {1 7 lock exclusive} {1 7 unlock exclusive} {0 1 unlock exclusive}