sl@0: sl@0: # 2007 Aug 13 sl@0: # sl@0: # The author disclaims copyright to this source code. In place of sl@0: # a legal notice, here is a blessing: sl@0: # sl@0: # May you do good and not evil. sl@0: # May you find forgiveness for yourself and forgive others. sl@0: # May you share freely, never taking more than you give. sl@0: # sl@0: #*********************************************************************** sl@0: # sl@0: # This file tests aspects of recovery from a malloc() failure sl@0: # in a CREATE INDEX statement. sl@0: # sl@0: # $Id: crash5.test,v 1.3 2008/07/12 14:52:20 drh Exp $ sl@0: sl@0: set testdir [file dirname $argv0] sl@0: source $testdir/tester.tcl sl@0: sl@0: # Only run these tests if memory debugging is turned on. sl@0: # sl@0: ifcapable !memdebug||!crashtest||!memorymanage { sl@0: puts "Skipping crash5 tests: not compiled with -DSQLITE_MEMDEBUG..." sl@0: finish_test sl@0: return sl@0: } sl@0: sl@0: db close sl@0: sl@0: for {set ii 0} {$ii < 10} {incr ii} { sl@0: for {set jj 50} {$jj < 100} {incr jj} { sl@0: sl@0: # Set up the database so that it is an auto-vacuum database sl@0: # containing a single table (root page 3) with a single row. sl@0: # The row has an overflow page (page 4). sl@0: file delete -force test.db test.db-journal sl@0: sqlite3 db test.db sl@0: set c [string repeat 3 1500] sl@0: db eval { sl@0: pragma auto_vacuum = 1; sl@0: CREATE TABLE t1(a, b, c); sl@0: INSERT INTO t1 VALUES('1111111111', '2222222222', $c); sl@0: } sl@0: db close sl@0: sl@0: do_test crash5-$ii.$jj.1 { sl@0: crashsql -delay 1 -file test.db-journal -seed $ii -tclbody [join [list \ sl@0: [list set iFail $jj] { sl@0: sqlite3_crashparams 0 [file join [pwd] test.db-journal] sl@0: sl@0: # Begin a transaction and evaluate a "CREATE INDEX" statement sl@0: # with the iFail'th malloc() set to fail. This operation will sl@0: # have to move the current contents of page 4 (the overflow sl@0: # page) to make room for the new root page. The bug is that sl@0: # if malloc() fails at a particular point in sqlite3PagerMovepage(), sl@0: # sqlite mistakenly thinks that the page being moved (page 4) has sl@0: # been safely synced into the journal. If the page is written sl@0: # to later in the transaction, it may be written out to the database sl@0: # before the relevant part of the journal has been synced. sl@0: # sl@0: db eval BEGIN sl@0: sqlite3_memdebug_fail $iFail -repeat 0 sl@0: catch {db eval { CREATE UNIQUE INDEX i1 ON t1(a); }} msg sl@0: # puts "$n $msg ac=[sqlite3_get_autocommit db]" sl@0: sl@0: # If the transaction is still active (it may not be if the malloc() sl@0: # failure occured in the OS layer), write to the database. Make sure sl@0: # page 4 is among those written. sl@0: # sl@0: if {![sqlite3_get_autocommit db]} { sl@0: db eval { sl@0: DELETE FROM t1; -- This will put page 4 on the free list. sl@0: INSERT INTO t1 VALUES('111111111', '2222222222', '33333333'); sl@0: INSERT INTO t1 SELECT * FROM t1; -- 2 sl@0: INSERT INTO t1 SELECT * FROM t1; -- 4 sl@0: INSERT INTO t1 SELECT * FROM t1; -- 8 sl@0: INSERT INTO t1 SELECT * FROM t1; -- 16 sl@0: INSERT INTO t1 SELECT * FROM t1; -- 32 sl@0: INSERT INTO t1 SELECT * FROM t1 WHERE rowid%2; -- 48 sl@0: } sl@0: } sl@0: sl@0: # If the right malloc() failed during the 'CREATE INDEX' above and sl@0: # the transaction was not rolled back, then the sqlite cache now sl@0: # has a dirty page 4 that it incorrectly believes is already safely sl@0: # in the synced part of the journal file. When sl@0: # sqlite3_release_memory() is called sqlite tries to free memory sl@0: # by writing page 4 out to the db file. If it crashes later on, sl@0: # before syncing the journal... Corruption! sl@0: # sl@0: sqlite3_crashparams 1 [file join [pwd] test.db-journal] sl@0: sqlite3_release_memory 8092 sl@0: }]] {} sl@0: expr 1 sl@0: } {1} sl@0: sl@0: sqlite3 db test.db sl@0: do_test crash5-$ii.$jj.2 { sl@0: db eval {pragma integrity_check} sl@0: } {ok} sl@0: do_test crash5-$ii.$jj.3 { sl@0: db eval {SELECT * FROM t1} sl@0: } [list 1111111111 2222222222 $::c] sl@0: db close sl@0: } sl@0: } sl@0: sl@0: sl@0: finish_test