Home | History | Annotate | Download | only in test
      1 
      2 # 2007 Aug 13
      3 #
      4 # The author disclaims copyright to this source code.  In place of
      5 # a legal notice, here is a blessing:
      6 #
      7 #    May you do good and not evil.
      8 #    May you find forgiveness for yourself and forgive others.
      9 #    May you share freely, never taking more than you give.
     10 #
     11 #***********************************************************************
     12 # 
     13 # This file tests aspects of recovery from a malloc() failure
     14 # in a CREATE INDEX statement.
     15 #
     16 # $Id: crash5.test,v 1.3 2008/07/12 14:52:20 drh Exp $
     17 
     18 set testdir [file dirname $argv0]
     19 source $testdir/tester.tcl
     20 
     21 # Only run these tests if memory debugging is turned on.
     22 #
     23 ifcapable !memdebug||!crashtest||!memorymanage {
     24    puts "Skipping crash5 tests: not compiled with -DSQLITE_MEMDEBUG..."
     25    finish_test
     26    return
     27 }
     28 
     29 db close
     30 
     31 for {set ii 0} {$ii < 10} {incr ii} {
     32   for {set jj 50} {$jj < 100} {incr jj} {
     33 
     34     # Set up the database so that it is an auto-vacuum database 
     35     # containing a single table (root page 3) with a single row. 
     36     # The row has an overflow page (page 4).
     37     file delete -force test.db test.db-journal
     38     sqlite3 db test.db
     39     set c [string repeat 3 1500]
     40     db eval {
     41       pragma auto_vacuum = 1;
     42       CREATE TABLE t1(a, b, c);
     43       INSERT INTO t1 VALUES('1111111111', '2222222222', $c);
     44     }
     45     db close
     46 
     47     do_test crash5-$ii.$jj.1 {
     48       crashsql -delay 1 -file test.db-journal -seed $ii -tclbody [join [list \
     49         [list set iFail $jj] {
     50         sqlite3_crashparams 0 [file join [pwd] test.db-journal]
     51       
     52         # Begin a transaction and evaluate a "CREATE INDEX" statement
     53         # with the iFail'th malloc() set to fail. This operation will
     54         # have to move the current contents of page 4 (the overflow
     55         # page) to make room for the new root page. The bug is that
     56         # if malloc() fails at a particular point in sqlite3PagerMovepage(),
     57         # sqlite mistakenly thinks that the page being moved (page 4) has 
     58         # been safely synced into the journal. If the page is written
     59         # to later in the transaction, it may be written out to the database
     60         # before the relevant part of the journal has been synced.
     61         #
     62         db eval BEGIN
     63         sqlite3_memdebug_fail $iFail -repeat 0
     64         catch {db eval { CREATE UNIQUE INDEX i1 ON t1(a); }} msg
     65         # puts "$n $msg ac=[sqlite3_get_autocommit db]"
     66       
     67         # If the transaction is still active (it may not be if the malloc()
     68         # failure occured in the OS layer), write to the database. Make sure
     69         # page 4 is among those written.
     70         #
     71         if {![sqlite3_get_autocommit db]} {
     72           db eval {
     73             DELETE FROM t1;  -- This will put page 4 on the free list.
     74             INSERT INTO t1 VALUES('111111111', '2222222222', '33333333');
     75             INSERT INTO t1 SELECT * FROM t1;                     -- 2
     76             INSERT INTO t1 SELECT * FROM t1;                     -- 4
     77             INSERT INTO t1 SELECT * FROM t1;                     -- 8
     78             INSERT INTO t1 SELECT * FROM t1;                     -- 16
     79             INSERT INTO t1 SELECT * FROM t1;                     -- 32
     80             INSERT INTO t1 SELECT * FROM t1 WHERE rowid%2;       -- 48
     81           }
     82         }
     83         
     84         # If the right malloc() failed during the 'CREATE INDEX' above and
     85         # the transaction was not rolled back, then the sqlite cache now 
     86         # has a dirty page 4 that it incorrectly believes is already safely
     87         # in the synced part of the journal file. When 
     88         # sqlite3_release_memory() is called sqlite tries to free memory
     89         # by writing page 4 out to the db file. If it crashes later on,
     90         # before syncing the journal... Corruption!
     91         #
     92         sqlite3_crashparams 1 [file join [pwd] test.db-journal]
     93         sqlite3_release_memory 8092
     94       }]] {}
     95       expr 1
     96     } {1}
     97   
     98     sqlite3 db test.db
     99     do_test crash5-$ii.$jj.2 {
    100       db eval {pragma integrity_check}
    101     } {ok}
    102     do_test crash5-$ii.$jj.3 {
    103       db eval {SELECT * FROM t1}
    104     } [list 1111111111 2222222222 $::c]
    105     db close
    106   }
    107 }
    108 
    109 
    110 finish_test
    111