15279112eSdan# 2011 August 08 25279112eSdan# 35279112eSdan# The author disclaims copyright to this source code. In place of 45279112eSdan# a legal notice, here is a blessing: 55279112eSdan# 65279112eSdan# May you do good and not evil. 75279112eSdan# May you find forgiveness for yourself and forgive others. 85279112eSdan# May you share freely, never taking more than you give. 95279112eSdan# 105279112eSdan#*********************************************************************** 115279112eSdan# 125279112eSdan 135279112eSdanset testdir [file dirname $argv0] 145279112eSdansource $testdir/tester.tcl 155279112eSdansource $testdir/lock_common.tcl 165279112eSdansource $testdir/malloc_common.tcl 175279112eSdan 18689ab897Sdanifcapable !mergesort { 19689ab897Sdan finish_test 20689ab897Sdan return 21689ab897Sdan} 22689ab897Sdan 235279112eSdanset testprefix indexfault 245279112eSdan 25262765a7Sdan# Set up the custom fault-injector. This is further configured by using 26262765a7Sdan# different values for $::custom_filter and different implementations 27262765a7Sdan# of Tcl proc [xCustom] for each test case. 28262765a7Sdan# 29262765a7Sdanproc install_custom_faultsim {} { 30262765a7Sdan set ::FAULTSIM(custom) [list \ 31262765a7Sdan -injectinstall custom_injectinstall \ 32262765a7Sdan -injectstart custom_injectstart \ 33262765a7Sdan -injectstop custom_injectstop \ 34262765a7Sdan -injecterrlist {{1 {disk I/O error}}} \ 35262765a7Sdan -injectuninstall custom_injectuninstall \ 36262765a7Sdan ] 37262765a7Sdan proc custom_injectinstall {} { 38262765a7Sdan testvfs shmfault -default true 39262765a7Sdan shmfault filter $::custom_filter 40262765a7Sdan shmfault script xCustom 41262765a7Sdan } 42262765a7Sdan proc custom_injectuninstall {} { 43262765a7Sdan catch {db close} 44262765a7Sdan catch {db2 close} 45262765a7Sdan shmfault delete 46262765a7Sdan } 47262765a7Sdan set ::custom_ifail -1 48262765a7Sdan set ::custom_nfail -1 49262765a7Sdan proc custom_injectstart {iFail} { 50262765a7Sdan set ::custom_ifail $iFail 51262765a7Sdan set ::custom_nfail 0 52262765a7Sdan } 53262765a7Sdan proc custom_injectstop {} { 54262765a7Sdan set ::custom_ifail -1 55262765a7Sdan return $::custom_nfail 56262765a7Sdan } 57262765a7Sdan} 58262765a7Sdanproc uninstall_custom_faultsim {} { 59262765a7Sdan unset -nocomplain ::FAULTSIM(custom) 60262765a7Sdan} 61262765a7Sdan 62262765a7Sdan 635279112eSdan#------------------------------------------------------------------------- 645279112eSdan# These tests - indexfault-1.* - Build an index on a smallish table with 655279112eSdan# all different kinds of fault-injection. The CREATE INDEX is run once 665279112eSdan# with default options and once with a 50KB soft-heap-limit. 675279112eSdan# 685279112eSdando_execsql_test 1.0 { 695279112eSdan BEGIN; 705279112eSdan CREATE TABLE t1(x); 715279112eSdan INSERT INTO t1 VALUES(randomblob(202)); 725279112eSdan INSERT INTO t1 SELECT randomblob(202) FROM t1; -- 2 735279112eSdan INSERT INTO t1 SELECT randomblob(202) FROM t1; -- 4 745279112eSdan INSERT INTO t1 SELECT randomblob(202) FROM t1; -- 8 755279112eSdan INSERT INTO t1 SELECT randomblob(202) FROM t1; -- 16 765279112eSdan INSERT INTO t1 SELECT randomblob(202) FROM t1; -- 32 775279112eSdan INSERT INTO t1 SELECT randomblob(202) FROM t1; -- 64 785279112eSdan INSERT INTO t1 SELECT randomblob(202) FROM t1; -- 128 795279112eSdan INSERT INTO t1 SELECT randomblob(202) FROM t1; -- 256 805279112eSdan COMMIT; 815279112eSdan} 825279112eSdanfaultsim_save_and_close 835279112eSdan 845279112eSdando_faultsim_test 1.1 -prep { 855279112eSdan faultsim_restore_and_reopen 865279112eSdan} -body { 875279112eSdan execsql { CREATE INDEX i1 ON t1(x) } 885279112eSdan faultsim_test_result {0 {}} 895279112eSdan faultsim_integrity_check 905279112eSdan} 915279112eSdanifcapable memorymanage { 925279112eSdan set soft_limit [sqlite3_soft_heap_limit 50000] 935279112eSdan do_faultsim_test 2.1 -prep { 945279112eSdan faultsim_restore_and_reopen 955279112eSdan } -body { 965279112eSdan execsql { CREATE INDEX i1 ON t1(x) } 975279112eSdan faultsim_test_result {0 {}} 985279112eSdan } 995279112eSdan sqlite3_soft_heap_limit $soft_limit 1005279112eSdan} 1015279112eSdan 1025279112eSdan#------------------------------------------------------------------------- 1035279112eSdan# These are similar to the indexfault-1.* tests, except they create an 1045279112eSdan# index with more than one column. 1055279112eSdan# 1065279112eSdansqlite3 db test.db 1075279112eSdando_execsql_test 2.0 { 1085279112eSdan BEGIN; 1095279112eSdan DROP TABLE IF EXISTS t1; 1105279112eSdan CREATE TABLE t1(t,u,v,w,x,y,z); 1115279112eSdan INSERT INTO t1 VALUES( 1125279112eSdan randomblob(30), randomblob(30), randomblob(30), randomblob(30), 1135279112eSdan randomblob(30), randomblob(30), randomblob(30) 1145279112eSdan ); 1155279112eSdan INSERT INTO t1 SELECT 1165279112eSdan randomblob(30), randomblob(30), randomblob(30), randomblob(30), 1175279112eSdan randomblob(30), randomblob(30), randomblob(30) FROM t1; -- 2 1185279112eSdan INSERT INTO t1 SELECT 1195279112eSdan randomblob(30), randomblob(30), randomblob(30), randomblob(30), 1205279112eSdan randomblob(30), randomblob(30), randomblob(30) FROM t1; -- 4 1215279112eSdan INSERT INTO t1 SELECT 1225279112eSdan randomblob(30), randomblob(30), randomblob(30), randomblob(30), 1235279112eSdan randomblob(30), randomblob(30), randomblob(30) FROM t1; -- 8 1245279112eSdan INSERT INTO t1 SELECT 1255279112eSdan randomblob(30), randomblob(30), randomblob(30), randomblob(30), 1265279112eSdan randomblob(30), randomblob(30), randomblob(30) FROM t1; -- 16 1275279112eSdan INSERT INTO t1 SELECT 1285279112eSdan randomblob(30), randomblob(30), randomblob(30), randomblob(30), 1295279112eSdan randomblob(30), randomblob(30), randomblob(30) FROM t1; -- 32 1305279112eSdan INSERT INTO t1 SELECT 1315279112eSdan randomblob(30), randomblob(30), randomblob(30), randomblob(30), 1325279112eSdan randomblob(30), randomblob(30), randomblob(30) FROM t1; -- 64 1335279112eSdan INSERT INTO t1 SELECT 1345279112eSdan randomblob(30), randomblob(30), randomblob(30), randomblob(30), 1355279112eSdan randomblob(30), randomblob(30), randomblob(30) FROM t1; -- 128 1365279112eSdan COMMIT; 1375279112eSdan} 1385279112eSdanfaultsim_save_and_close 1395279112eSdan 1405279112eSdando_faultsim_test 2.1 -prep { 1415279112eSdan faultsim_restore_and_reopen 1425279112eSdan} -body { 1435279112eSdan execsql { CREATE INDEX i1 ON t1(t,u,v,w,x,y,z) } 1445279112eSdan faultsim_test_result {0 {}} 1455279112eSdan faultsim_integrity_check 1465279112eSdan} 1475279112eSdanifcapable memorymanage { 1485279112eSdan set soft_limit [sqlite3_soft_heap_limit 50000] 1495279112eSdan do_faultsim_test 2.2 -prep { 1505279112eSdan faultsim_restore_and_reopen 1515279112eSdan } -body { 1525279112eSdan execsql { CREATE INDEX i1 ON t1(t,u,v,w,x,y,z) } 1535279112eSdan faultsim_test_result {0 {}} 1545279112eSdan } 1555279112eSdan sqlite3_soft_heap_limit $soft_limit 1565279112eSdan} 1575279112eSdan 1585279112eSdan#------------------------------------------------------------------------- 1595279112eSdan# The following tests - indexfault-2.* - all attempt to build a index 1605279112eSdan# on table t1 in the main database with injected IO errors. Individual 1615279112eSdan# test cases work as follows: 1625279112eSdan# 1635279112eSdan# 3.1: IO errors injected into xOpen() calls. 1645279112eSdan# 3.2: As 7.1, but with a low (50KB) soft-heap-limit. 1655279112eSdan# 1665279112eSdan# 3.3: IO errors injected into the first 200 write() calls made on the 1675279112eSdan# second temporary file. 1685279112eSdan# 3.4: As 7.3, but with a low (50KB) soft-heap-limit. 1695279112eSdan# 170262765a7Sdan# 3.5: After a certain amount of data has been read from the main database 171262765a7Sdan# file (and written into the temporary b-tree), sqlite3_release_memory() 172262765a7Sdan# is called to free as much memory as possible. This causes the temp 173262765a7Sdan# b-tree to be flushed to disk. So that before its contents can be 174262765a7Sdan# transfered to a PMA they must be read back from disk - creating extra 175262765a7Sdan# opportunities for IO errors. 1765279112eSdan# 177262765a7Sdaninstall_custom_faultsim 1785279112eSdan 1795279112eSdan# Set up a table to build indexes on. Save the setup using the 1805279112eSdan# [faultsim_save_and_close] mechanism. 1815279112eSdan# 1825279112eSdansqlite3 db test.db 1835279112eSdando_execsql_test 3.0 { 1845279112eSdan BEGIN; 1855279112eSdan DROP TABLE IF EXISTS t1; 1865279112eSdan CREATE TABLE t1(x); 1875279112eSdan INSERT INTO t1 VALUES(randomblob(11000)); 1885279112eSdan INSERT INTO t1 SELECT randomblob(11001) FROM t1; -- 2 1895279112eSdan INSERT INTO t1 SELECT randomblob(11002) FROM t1; -- 4 1905279112eSdan INSERT INTO t1 SELECT randomblob(11003) FROM t1; -- 8 1915279112eSdan INSERT INTO t1 SELECT randomblob(11004) FROM t1; -- 16 1925279112eSdan INSERT INTO t1 SELECT randomblob(11005) FROM t1; -- 32 1935279112eSdan INSERT INTO t1 SELECT randomblob(11006) FROM t1; -- 64 1945279112eSdan INSERT INTO t1 SELECT randomblob(11007) FROM t1; -- 128 1955279112eSdan INSERT INTO t1 SELECT randomblob(11008) FROM t1; -- 256 1965279112eSdan INSERT INTO t1 SELECT randomblob(11009) FROM t1; -- 512 1975279112eSdan COMMIT; 1985279112eSdan} 1995279112eSdanfaultsim_save_and_close 2005279112eSdan 2015279112eSdanset ::custom_filter xOpen 2025279112eSdanproc xCustom {args} { 2035279112eSdan incr ::custom_ifail -1 2045279112eSdan if {$::custom_ifail==0} { 2055279112eSdan incr ::custom_nfail 2065279112eSdan return "SQLITE_IOERR" 2075279112eSdan } 2085279112eSdan return "SQLITE_OK" 2095279112eSdan} 2105279112eSdando_faultsim_test 3.1 -faults custom -prep { 2115279112eSdan faultsim_restore_and_reopen 2125279112eSdan} -body { 2135279112eSdan execsql { CREATE INDEX i1 ON t1(x) } 2145279112eSdan faultsim_test_result {0 {}} 2155279112eSdan} 2165279112eSdanifcapable memorymanage { 2175279112eSdan set soft_limit [sqlite3_soft_heap_limit 50000] 2185279112eSdan do_faultsim_test 3.2 -faults custom -prep { 2195279112eSdan faultsim_restore_and_reopen 2205279112eSdan } -body { 2215279112eSdan execsql { CREATE INDEX i1 ON t1(x) } 2225279112eSdan faultsim_test_result {0 {}} 2235279112eSdan } 2245279112eSdan sqlite3_soft_heap_limit $soft_limit 2255279112eSdan} 2265279112eSdan 2275279112eSdanset ::custom_filter {xOpen xWrite} 2285279112eSdanproc xCustom {method args} { 2295279112eSdan if {$method == "xOpen"} { 2305279112eSdan if {[lindex $args 0] == ""} { 2315279112eSdan incr ::nTmpOpen 1 2325279112eSdan if {$::nTmpOpen == 3} { return "failme" } 2335279112eSdan } 2345279112eSdan return "SQLITE_OK" 2355279112eSdan } 2365279112eSdan if {$::custom_ifail<200 && [lindex $args 1] == "failme"} { 2375279112eSdan incr ::custom_ifail -1 2385279112eSdan if {$::custom_ifail==0} { 2395279112eSdan incr ::custom_nfail 2405279112eSdan return "SQLITE_IOERR" 2415279112eSdan } 2425279112eSdan } 2435279112eSdan return "SQLITE_OK" 2445279112eSdan} 2455279112eSdan 2465279112eSdando_faultsim_test 3.3 -faults custom -prep { 2475279112eSdan faultsim_restore_and_reopen 2485279112eSdan set ::nTmpOpen 0 2495279112eSdan} -body { 2505279112eSdan execsql { CREATE INDEX i1 ON t1(x) } 2515279112eSdan faultsim_test_result {0 {}} 2525279112eSdan} 2535279112eSdan 2545279112eSdanifcapable memorymanage { 2555279112eSdan set soft_limit [sqlite3_soft_heap_limit 50000] 2565279112eSdan do_faultsim_test 3.4 -faults custom -prep { 2575279112eSdan faultsim_restore_and_reopen 2585279112eSdan set ::nTmpOpen 0 2595279112eSdan } -body { 2605279112eSdan execsql { CREATE INDEX i1 ON t1(x) } 2615279112eSdan faultsim_test_result {0 {}} 2625279112eSdan } 2635279112eSdan sqlite3_soft_heap_limit $soft_limit 2645279112eSdan} 2655279112eSdan 266262765a7Sdanuninstall_custom_faultsim 267262765a7Sdan 268262765a7Sdan#------------------------------------------------------------------------- 269262765a7Sdan# Test 4: After a certain amount of data has been read from the main database 270262765a7Sdan# file (and written into the temporary b-tree), sqlite3_release_memory() is 271262765a7Sdan# called to free as much memory as possible. This causes the temp b-tree to be 272262765a7Sdan# flushed to disk. So that before its contents can be transfered to a PMA they 273262765a7Sdan# must be read back from disk - creating extra opportunities for IO errors. 274262765a7Sdan# 275262765a7Sdaninstall_custom_faultsim 276262765a7Sdan 277262765a7Sdancatch { db close } 278262765a7Sdanforcedelete test.db 279262765a7Sdansqlite3 db test.db 280262765a7Sdan 281262765a7Sdando_execsql_test 4.0 { 282262765a7Sdan BEGIN; 283262765a7Sdan DROP TABLE IF EXISTS t1; 284262765a7Sdan CREATE TABLE t1(x); 285262765a7Sdan INSERT INTO t1 VALUES(randomblob(11000)); 286262765a7Sdan INSERT INTO t1 SELECT randomblob(11001) FROM t1; -- 2 287262765a7Sdan INSERT INTO t1 SELECT randomblob(11002) FROM t1; -- 4 288262765a7Sdan INSERT INTO t1 SELECT randomblob(11003) FROM t1; -- 8 289262765a7Sdan INSERT INTO t1 SELECT randomblob(11004) FROM t1; -- 16 290262765a7Sdan INSERT INTO t1 SELECT randomblob(11005) FROM t1; -- 32 291262765a7Sdan INSERT INTO t1 SELECT randomblob(11005) FROM t1; -- 64 292262765a7Sdan COMMIT; 293262765a7Sdan} 294262765a7Sdanfaultsim_save_and_close 295262765a7Sdan 296262765a7Sdantestvfs tvfs 297262765a7Sdantvfs script xRead 298262765a7Sdantvfs filter xRead 299262765a7Sdanset ::nRead 0 300262765a7Sdanproc xRead {method file args} { 301262765a7Sdan if {[file tail $file] == "test.db"} { incr ::nRead } 302262765a7Sdan} 303262765a7Sdan 304262765a7Sdando_test 4.1 { 305262765a7Sdan sqlite3 db test.db -vfs tvfs 306262765a7Sdan execsql { CREATE INDEX i1 ON t1(x) } 307262765a7Sdan} {} 308262765a7Sdan 309262765a7Sdandb close 310262765a7Sdantvfs delete 311262765a7Sdan 312262765a7Sdanset ::custom_filter xRead 313262765a7Sdanproc xCustom {method file args} { 314262765a7Sdan incr ::nReadCall 315262765a7Sdan if {$::nReadCall >= ($::nRead/5)} { 316262765a7Sdan if {$::nReadCall == ($::nRead/5)} { 317262765a7Sdan set nByte [sqlite3_release_memory [expr 64*1024*1024]] 318262765a7Sdan sqlite3_soft_heap_limit 20000 319262765a7Sdan } 320262765a7Sdan if {$file == ""} { 321262765a7Sdan incr ::custom_ifail -1 322262765a7Sdan if {$::custom_ifail==0} { 323262765a7Sdan incr ::custom_nfail 324262765a7Sdan return "SQLITE_IOERR" 325262765a7Sdan } 326262765a7Sdan } 327262765a7Sdan } 328262765a7Sdan return "SQLITE_OK" 329262765a7Sdan} 330262765a7Sdan 331262765a7Sdando_faultsim_test 4.2 -faults custom -prep { 332262765a7Sdan faultsim_restore_and_reopen 333262765a7Sdan set ::nReadCall 0 334262765a7Sdan sqlite3_soft_heap_limit 0 335262765a7Sdan} -body { 336262765a7Sdan execsql { CREATE INDEX i1 ON t1(x) } 337262765a7Sdan faultsim_test_result {0 {}} 338262765a7Sdan} 339262765a7Sdan 340*480c572fSdrhdo_faultsim_test 5 -prep { 341*480c572fSdrh reset_db 342*480c572fSdrh} -body { 343*480c572fSdrh execsql { 344*480c572fSdrh CREATE TABLE reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongname(a PRIMARY KEY) WITHOUT ROWID; 345*480c572fSdrh } 346*480c572fSdrh} -test { 347*480c572fSdrh faultsim_test_result {0 {}} 348*480c572fSdrh} 349*480c572fSdrh 350262765a7Sdanuninstall_custom_faultsim 351262765a7Sdan 3525279112eSdanfinish_test 353