/ Check-in [0b82f962]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add test file for new malloc() failure handling. (CVS 2801)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 0b82f9623cf25b1cb02f70628c992903a6e8ca1c
User & Date: danielk1977 2005-12-06 12:57:59
Context
2005-12-06
13:19
Report errors out of sqlite3_open16(). (CVS 2802) check-in: f5b58163 user: drh tags: trunk
12:57
Add test file for new malloc() failure handling. (CVS 2801) check-in: 0b82f962 user: danielk1977 tags: trunk
12:52
Some elements of the new malloc() failure handling. Not all cases work properly yet. Also, library is not threadsafe if malloc() fails right now. (CVS 2800) check-in: e1606658 user: danielk1977 tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Added test/malloc3.test.

            1  +# 2005 November 30
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +# $Id: malloc3.test,v 1.1 2005/12/06 12:57:59 danielk1977 Exp $
           13  +
           14  +set testdir [file dirname $argv0]
           15  +source $testdir/tester.tcl
           16  +
           17  +# Only run these tests if memory debugging is turned on.
           18  +if {[info command sqlite_malloc_stat]==""} {
           19  +   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
           20  +   finish_test
           21  +   return
           22  +}
           23  +
           24  +#--------------------------------------------------------------------------
           25  +# NOTES ON RECOVERING FROM A MALLOC FAILURE
           26  +# 
           27  +# The tests in this file test the behaviours described in the following
           28  +# paragraphs. These tests test the behaviour of the system when malloc() fails
           29  +# inside of a call to _prepare(), _step(), _finalize() or _reset(). The
           30  +# handling of malloc() failures within ancillary procedures is tested
           31  +# elsewhere.
           32  +#
           33  +# Overview:
           34  +#
           35  +# Executing a statement is done in three stages (prepare, step and finalize). A
           36  +# malloc() failure may occur within any stage. If a memory allocation fails
           37  +# during statement preparation, no statement handle is returned. From the users
           38  +# point of view the system state is as if _prepare() had never been called.
           39  +#
           40  +# If the memory allocation fails during the _step() or _finalize() calls, then
           41  +# the database may be left in one of two states (after finalize() has been
           42  +# called):
           43  +#
           44  +#     * As if the neither _step() nor _finalize() had ever been called on
           45  +#       the statement handle (i.e. any changes made by the statement are
           46  +#       rolled back).
           47  +#     * The current transaction may be rolled back. In this case a hot-journal
           48  +#       may or may not actually be present in the filesystem.
           49  +#
           50  +# The caller can tell the difference between these two scenarios by invoking
           51  +# _get_autocommit().
           52  +#
           53  +#
           54  +# Handling of sqlite3_reset():
           55  +#
           56  +# If a malloc() fails while executing an sqlite3_reset() call, this is handled
           57  +# in the same way as a failure within _finalize(). The statement handle
           58  +# is not deleted and must be passed to _finalize() for resource deallocation.
           59  +# Attempting to _step() or _reset() the statement after a failed _reset() will
           60  +# always return SQLITE_NOMEM.
           61  +#
           62  +#
           63  +# Other active SQL statements:
           64  +#
           65  +# The effect of a malloc failure on concurrently executing SQL statements,
           66  +# particularly when the statement is executing with READ_UNCOMMITTED set and
           67  +# the malloc() failure mandates statement rollback only. Currently, if
           68  +# transaction rollback is required, all other vdbe's are aborted.
           69  +#
           70  +#     Non-transient mallocs in btree.c:
           71  +#         * The Btree structure itself
           72  +#         * Each BtCursor structure
           73  +#
           74  +#     Mallocs in pager.c:
           75  +#         readMasterJournal()  - Space to read the master journal name
           76  +#         pager_delmaster()    - Space for the entire master journal file
           77  +#
           78  +#         sqlite3pager_open()  - The pager structure itself
           79  +#         sqlite3_pagerget()   - Space for a new page
           80  +#         pager_open_journal() - Pager.aInJournal[] bitmap
           81  +#         sqlite3pager_write() - For in-memory databases only: history page and
           82  +#                                statement history page.
           83  +#         pager_stmt_begin()   - Pager.aInStmt[] bitmap
           84  +#
           85  +# None of the above are a huge problem. The most troublesome failures are the
           86  +# transient malloc() calls in btree.c, which can occur during the tree-balance
           87  +# operation. This means the tree being balanced will be internally inconsistent
           88  +# after the malloc() fails. To avoid the corrupt tree being read by a
           89  +# READ_UNCOMMITTED query, we have to make sure the transaction or statement
           90  +# rollback occurs before sqlite3_step() returns, not during a subsequent
           91  +# sqlite3_finalize().
           92  +#--------------------------------------------------------------------------
           93  +
           94  +#--------------------------------------------------------------------------
           95  +# NOTES ON TEST IMPLEMENTATION
           96  +#
           97  +# The tests in this file are implemented differently from those in other
           98  +# files. Instead, tests are specified using three primitives: SQL, PREP and
           99  +# TEST. Each primitive has a single argument. Primitives are processed in
          100  +# the order they are specified in the file.
          101  +#
          102  +# A TEST primitive specifies a TCL script as it's argument. When a TEST
          103  +# directive is encountered the Tcl script is evaluated. Usually, this Tcl
          104  +# script contains one or more calls to [do_test].
          105  +#
          106  +# A PREP primitive specifies an SQL script as it's argument. When a PREP
          107  +# directive is encountered the SQL is evaluated using database connection
          108  +# [db].
          109  +#
          110  +# The SQL primitives are where the action happens. An SQL primitive must
          111  +# contain a single, valid SQL statement as it's argument. When an SQL
          112  +# primitive is encountered, it is evaluated one or more times to test the
          113  +# behaviour of the system when malloc() fails during preparation or
          114  +# execution of said statement. The Nth time the statement is executed,
          115  +# the Nth malloc is said to fail. The statement is executed until it
          116  +# succeeds, i.e. (M+1) times, where M is the number of mallocs() required
          117  +# to prepare and execute the statement.
          118  +#
          119  +# Each time an SQL statement fails, the driver program (see proc [run_test]
          120  +# below) figures out if a transaction has been automatically rolled back.
          121  +# If not, it executes any TEST block immediately proceeding the SQL
          122  +# statement, then reexecutes the SQL statement with the next value of N.
          123  +#
          124  +# If a transaction has been automatically rolled back, then the driver
          125  +# program executes all the SQL specified as part of SQL or PREP primitives
          126  +# between the current SQL statement and the most recent "BEGIN". Any 
          127  +# TEST block immediately proceeding the SQL statement is evaluated, and
          128  +# then the SQL statement reexecuted with the incremented N value.
          129  +#
          130  +# That make any sense? If not, read the code in [run_test] and it might.
          131  +#
          132  +# Extra restriction imposed by the implementation:
          133  +#
          134  +# * If a PREP block starts a transaction, it must finish it.
          135  +# * A PREP block may not close a transaction it did not start.
          136  +#
          137  +#--------------------------------------------------------------------------
          138  +
          139  +
          140  +# These procs are used to build up a "program" in global variable
          141  +# ::run_test_script. At the end of this file, the proc [run_test] is used
          142  +# to execute the program (and all test cases contained therein).
          143  +#
          144  +proc TEST {id t} {lappend ::run_test_script -test [list $id $t]}
          145  +proc PREP {p} {lappend ::run_test_script -prep [string trim $p]}
          146  +
          147  +# SQL --
          148  +#
          149  +#     SQL ?-norollback? <sql-text>
          150  +#
          151  +# Add an 'SQL' primitive to the program (see notes above). If the -norollback
          152  +# switch is present, then the statement is not allowed to automatically roll
          153  +# back any active transaction if malloc() fails. It must rollback the statement
          154  +# transaction only.
          155  +#
          156  +proc SQL  {a1 {a2 ""}} {
          157  +  # An SQL primitive parameter is a list of two elements, a boolean value
          158  +  # indicating if the statement may cause transaction rollback when malloc()
          159  +  # fails, and the sql statement itself.
          160  +  if {$a2 == ""} {
          161  +    lappend ::run_test_script -sql [list true [string trim $a1]]
          162  +  } else {
          163  +    lappend ::run_test_script -sql [list false [string trim $a2]]
          164  +  }
          165  +}
          166  +
          167  +# TEST_AUTOCOMMIT --
          168  +# 
          169  +#     A shorthand test to see if a transaction is active or not. The first
          170  +#     argument - $id - is the integer number of the test case. The second
          171  +#     argument is either 1 or 0, the expected value of the auto-commit flag.
          172  +#
          173  +proc TEST_AUTOCOMMIT {id a} {
          174  +    TEST $id "do_test \$testid { sqlite3_get_autocommit $::DB } {$a}"
          175  +}
          176  +
          177  +#--------------------------------------------------------------------------
          178  +# Start of test program declaration
          179  +#
          180  +
          181  +
          182  +# Warm body test. A malloc() fails in the middle of a CREATE TABLE statement
          183  +# in a single-statement transaction on an empty database. Not too much can go
          184  +# wrong here.
          185  +#
          186  +TEST 1 {
          187  +  do_test $testid {
          188  +    execsql {SELECT tbl_name FROM sqlite_master;}
          189  +  } {}
          190  +}
          191  +SQL { 
          192  +  CREATE TABLE abc(a, b, c); 
          193  +}
          194  +TEST 2 {
          195  +  do_test $testid.1 {
          196  +    execsql {SELECT tbl_name FROM sqlite_master;}
          197  +  } {abc}
          198  +}
          199  +
          200  +# Insert a couple of rows into the table. each insert is in it's own
          201  +# transaction. test that the table is unpopulated before running the inserts
          202  +# (and hence after each failure of the first insert), and that it has been
          203  +# populated correctly after the final insert succeeds.
          204  +#
          205  +TEST 3 {
          206  +  do_test $testid.2 {
          207  +    execsql {SELECT * FROM abc}
          208  +  } {}
          209  +}
          210  +SQL {INSERT INTO abc VALUES(1, 2, 3);}
          211  +SQL {INSERT INTO abc VALUES(4, 5, 6);}
          212  +SQL {INSERT INTO abc VALUES(7, 8, 9);}
          213  +TEST 4 {
          214  +  do_test $testid {
          215  +    execsql {SELECT * FROM abc}
          216  +  } {1 2 3 4 5 6 7 8 9}
          217  +}
          218  +
          219  +# Test a CREATE INDEX statement. Because the table 'abc' is so small, the index
          220  +# will all fit on a single page, so this doesn't test too much that the CREATE
          221  +# TABLE statement didn't test. A few of the transient malloc()s in btree.c
          222  +# perhaps.
          223  +#
          224  +SQL {CREATE INDEX abc_i ON abc(a, b, c);}
          225  +TEST 4 {
          226  +  do_test $testid {
          227  +    execsql {
          228  +      SELECT * FROM abc ORDER BY a DESC;
          229  +    }
          230  +  } {7 8 9 4 5 6 1 2 3}
          231  +}
          232  +
          233  +# Test a DELETE statement. Also create a trigger and a view, just to make sure
          234  +# these statements don't have any obvious malloc() related bugs in them. Note
          235  +# that the test above will be executed each time the DELETE fails, so we're
          236  +# also testing rollback of a DELETE from a table with an index on it.
          237  +#
          238  +SQL {DELETE FROM abc WHERE a > 2;}
          239  +SQL {CREATE TRIGGER abc_t AFTER INSERT ON abc BEGIN SELECT 'trigger!'; END;}
          240  +SQL {CREATE VIEW abc_v AS SELECT * FROM abc;}
          241  +TEST 5 {
          242  +  do_test $testid {
          243  +    execsql {
          244  +      SELECT name, tbl_name FROM sqlite_master ORDER BY name;
          245  +      SELECT * FROM abc;
          246  +    }
          247  +  } {abc abc abc_i abc abc_t abc abc_v abc_v 1 2 3}
          248  +}
          249  +
          250  +set sql {
          251  +  BEGIN;DELETE FROM abc;
          252  +}
          253  +for {set i 1} {$i < 100} {incr i} {
          254  +  set a $i
          255  +  set b "String value $i"
          256  +  set c [string repeat X $i]
          257  +  append sql "INSERT INTO abc VALUES ($a, '$b', '$c');"
          258  +}
          259  +append sql {COMMIT;}
          260  +PREP $sql
          261  +
          262  +SQL {
          263  +  DELETE FROM abc WHERE oid IN (SELECT oid FROM abc ORDER BY random() LIMIT 5);
          264  +}
          265  +TEST 6 {
          266  +  do_test $testid.1 {
          267  +    execsql {SELECT count(*) FROM abc}
          268  +  } {94}
          269  +  do_test $testid.2 {
          270  +    execsql {
          271  +      SELECT min(
          272  +          (oid == a) AND 'String value ' || a == b AND a == length(c) 
          273  +      ) FROM abc;
          274  +    }
          275  +  } {1}
          276  +}
          277  +SQL {
          278  +  DELETE FROM abc WHERE oid IN (SELECT oid FROM abc ORDER BY random() LIMIT 5);
          279  +}
          280  +TEST 7 {
          281  +  do_test $testid {
          282  +    execsql {SELECT count(*) FROM abc}
          283  +  } {89}
          284  +  do_test $testid {
          285  +    execsql {
          286  +      SELECT min(
          287  +          (oid == a) AND 'String value ' || a == b AND a == length(c) 
          288  +      ) FROM abc;
          289  +    }
          290  +  } {1}
          291  +}
          292  +SQL {
          293  +  DELETE FROM abc WHERE oid IN (SELECT oid FROM abc ORDER BY random() LIMIT 5);
          294  +}
          295  +TEST 9 {
          296  +  do_test $testid {
          297  +    execsql {SELECT count(*) FROM abc}
          298  +  } {84}
          299  +  do_test $testid {
          300  +    execsql {
          301  +      SELECT min(
          302  +          (oid == a) AND 'String value ' || a == b AND a == length(c) 
          303  +      ) FROM abc;
          304  +    }
          305  +  } {1}
          306  +}
          307  +
          308  +set padding [string repeat X 500]
          309  +PREP [subst {
          310  +  DROP TABLE abc;
          311  +  CREATE TABLE abc(a PRIMARY KEY, padding, b, c);
          312  +  INSERT INTO abc VALUES(0, '$padding', 2, 2);
          313  +  INSERT INTO abc VALUES(3, '$padding', 5, 5);
          314  +  INSERT INTO abc VALUES(6, '$padding', 8, 8);
          315  +}]
          316  +
          317  +TEST 10 {
          318  +  do_test $testid {
          319  +    execsql {SELECT a, b, c FROM abc}
          320  +  } {0 2 2 3 5 5 6 8 8}
          321  +}
          322  +
          323  +SQL {BEGIN;}
          324  +SQL {INSERT INTO abc VALUES(9, 'XXXXX', 11, 12);}
          325  +TEST_AUTOCOMMIT 11 0
          326  +SQL -norollback {UPDATE abc SET a = a + 1, c = c + 1;}
          327  +TEST_AUTOCOMMIT 12 0
          328  +SQL {DELETE FROM abc WHERE a = 10;}
          329  +TEST_AUTOCOMMIT 13 0
          330  +SQL {COMMIT;}
          331  +
          332  +TEST 14 {
          333  +  do_test $testid.1 {
          334  +    sqlite3_get_autocommit $::DB
          335  +  } {1}
          336  +  do_test $testid.2 {
          337  +    execsql {SELECT a, b, c FROM abc}
          338  +  } {1 2 3 4 5 6 7 8 9}
          339  +}
          340  +
          341  +PREP [subst {
          342  +  DROP TABLE abc;
          343  +  CREATE TABLE abc(a, padding, b, c);
          344  +  INSERT INTO abc VALUES(1, '$padding', 2, 3);
          345  +  INSERT INTO abc VALUES(4, '$padding', 5, 6);
          346  +  INSERT INTO abc VALUES(7, '$padding', 8, 9);
          347  +  CREATE INDEX abc_i ON abc(a, padding, b, c);
          348  +}]
          349  +
          350  +TEST 15 {
          351  +  db eval {PRAGMA cache_size = 10}
          352  +}
          353  +
          354  +SQL {BEGIN;}
          355  +SQL -norllbck {INSERT INTO abc (oid, a, padding, b, c) SELECT NULL, * FROM abc}
          356  +TEST 16 {
          357  +  do_test $testid {
          358  +    execsql {SELECT a, count(*) FROM abc GROUP BY a;}
          359  +  } {1 2 4 2 7 2}
          360  +}
          361  +SQL -norllbck {INSERT INTO abc (oid, a, padding, b, c) SELECT NULL, * FROM abc}
          362  +TEST 17 {
          363  +  do_test $testid {
          364  +    execsql {SELECT a, count(*) FROM abc GROUP BY a;}
          365  +  } {1 4 4 4 7 4}
          366  +}
          367  +SQL -norllbck {INSERT INTO abc (oid, a, padding, b, c) SELECT NULL, * FROM abc}
          368  +TEST 18 {
          369  +  do_test $testid {
          370  +    execsql {SELECT a, count(*) FROM abc GROUP BY a;}
          371  +  } {1 8 4 8 7 8}
          372  +}
          373  +SQL -norllbck {INSERT INTO abc (oid, a, padding, b, c) SELECT NULL, * FROM abc}
          374  +TEST 19 {
          375  +  do_test $testid {
          376  +    execsql {SELECT a, count(*) FROM abc GROUP BY a;}
          377  +  } {1 16 4 16 7 16}
          378  +}
          379  +SQL {COMMIT;}
          380  +TEST 21 {
          381  +  do_test $testid {
          382  +    execsql {SELECT a, count(*) FROM abc GROUP BY a;}
          383  +  } {1 16 4 16 7 16}
          384  +}
          385  +
          386  +SQL {BEGIN;}
          387  +SQL {DELETE FROM abc WHERE oid %2}
          388  +TEST 22 {
          389  +  do_test $testid {
          390  +    execsql {SELECT a, count(*) FROM abc GROUP BY a;}
          391  +  } {1 8 4 8 7 8}
          392  +}
          393  +SQL {DELETE FROM abc}
          394  +TEST 23 {
          395  +  do_test $testid {
          396  +    execsql {SELECT * FROM abc}
          397  +  } {}
          398  +}
          399  +SQL {ROLLBACK;}
          400  +TEST 24 {
          401  +  do_test $testid {
          402  +    execsql {SELECT a, count(*) FROM abc GROUP BY a;}
          403  +  } {1 16 4 16 7 16}
          404  +}
          405  +
          406  +# Test some schema modifications inside of a transaction. These should all
          407  +# cause transaction rollback if they fail. Also query a view, to cover a bit
          408  +# more code.
          409  +#
          410  +PREP {DROP VIEW abc_v;}
          411  +TEST 25 {
          412  +  do_test $testid {
          413  +    execsql {
          414  +      SELECT name, tbl_name FROM sqlite_master;
          415  +    }
          416  +  } {abc abc abc_i abc}
          417  +}
          418  +SQL {BEGIN;}
          419  +SQL {CREATE TABLE def(d, e, f);}
          420  +SQL {CREATE TABLE ghi(g, h, i);}
          421  +TEST 26 {
          422  +  do_test $testid {
          423  +    execsql {
          424  +      SELECT name, tbl_name FROM sqlite_master;
          425  +    }
          426  +  } {abc abc abc_i abc def def ghi ghi}
          427  +}
          428  +SQL {CREATE VIEW v1 AS SELECT * FROM def, ghi}
          429  +SQL {CREATE UNIQUE INDEX ghi_i1 ON ghi(g);}
          430  +TEST 27 {
          431  +  do_test $testid {
          432  +    execsql {
          433  +      SELECT name, tbl_name FROM sqlite_master;
          434  +    }
          435  +  } {abc abc abc_i abc def def ghi ghi v1 v1 ghi_i1 ghi}
          436  +}
          437  +SQL {INSERT INTO def VALUES('a', 'b', 'c')}
          438  +SQL {INSERT INTO def VALUES(1, 2, 3)}
          439  +SQL -norollback {INSERT INTO ghi SELECT * FROM def}
          440  +TEST 28 {
          441  +  do_test $testid {
          442  +    execsql {
          443  +      SELECT * FROM def, ghi WHERE d = g;
          444  +    }
          445  +  } {a b c a b c 1 2 3 1 2 3}
          446  +}
          447  +SQL {COMMIT}
          448  +TEST 29 {
          449  +  do_test $testid {
          450  +    execsql {
          451  +      SELECT * FROM v1 WHERE d = g;
          452  +    }
          453  +  } {a b c a b c 1 2 3 1 2 3}
          454  +}
          455  +
          456  +# Test a simple multi-file transaction
          457  +#
          458  +file delete -force test2.db
          459  +PREP {ATTACH 'test2.db' AS aux;}
          460  +SQL {BEGIN}
          461  +SQL {CREATE TABLE aux.tbl2(x, y, z)}
          462  +SQL {INSERT INTO tbl2 VALUES(1, 2, 3)}
          463  +SQL {INSERT INTO def VALUES(4, 5, 6)}
          464  +TEST 30 {
          465  +  do_test $testid {
          466  +    execsql {
          467  +      SELECT * FROM tbl2, def WHERE d = x;
          468  +    }
          469  +  } {1 2 3 1 2 3}
          470  +}
          471  +SQL {COMMIT}
          472  +TEST 31 {
          473  +  do_test $testid {
          474  +    execsql {
          475  +      SELECT * FROM tbl2, def WHERE d = x;
          476  +    }
          477  +  } {1 2 3 1 2 3}
          478  +}
          479  +
          480  +#
          481  +# End of test program declaration
          482  +#--------------------------------------------------------------------------
          483  +
          484  +proc run_test {arglist {pcstart 0} {iFailStart 1}} {
          485  +  if {[llength $arglist] %2} {
          486  +    error "Uneven number of arguments to TEST"
          487  +  }
          488  +
          489  +  for {set i 0} {$i < $pcstart} {incr i} {
          490  +    set k2 [lindex $arglist [expr 2 * $i]]
          491  +    set v2 [lindex $arglist [expr 2 * $i + 1]]
          492  +    set ac [sqlite3_get_autocommit $::DB]        ;# Auto-Commit
          493  +# puts "STARTUP"
          494  +    switch -- $k2 {
          495  +      -sql  {db eval [lindex $v2 1]}
          496  +      -prep {db eval $v2}
          497  +    }
          498  +    set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 
          499  +    if {$ac && !$nac} {set begin_pc $i}
          500  +  }
          501  +
          502  +  set iFail $iFailStart
          503  +  set pc $pcstart
          504  +  while {$pc*2 < [llength $arglist]} {
          505  +
          506  +    # Id of this iteration:
          507  +    set iterid "(pc $pc).(iFail $iFail)"
          508  +
          509  +    set k [lindex $arglist [expr 2 * $pc]]
          510  +    set v [lindex $arglist [expr 2 * $pc + 1]]
          511  +
          512  +    switch -- $k {
          513  +
          514  +      -test { 
          515  +        foreach {id script} $v {}
          516  +        set testid "malloc3-(test $id).$iterid"
          517  +        eval $script
          518  +        incr pc
          519  +      }
          520  +
          521  +      -sql {
          522  +        set ac [sqlite3_get_autocommit $::DB]        ;# Auto-Commit
          523  +        sqlite_malloc_fail $iFail
          524  +# puts "SQL $iterid [lindex $v 1]"
          525  +        set rc [catch {db eval [lindex $v 1]} msg]   ;# True error occurs
          526  +# puts "rc = $rc msg = \"$msg\""
          527  +        set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 
          528  +
          529  +        if {$rc == 0} {
          530  +            # Successful execution of sql.
          531  +            if {[lindex [sqlite_malloc_stat] 2] <= 0} {
          532  +              error "Unreported malloc() failure"
          533  +            }
          534  +            if {$ac && !$nac} {
          535  +              set begin_pc $pc
          536  +            } 
          537  +            incr pc
          538  +            set iFail 1
          539  +            sqlite_malloc_fail 0
          540  +            integrity_check "malloc3-(integrity).$iterid"
          541  +        } elseif {[regexp {.*out of memory} $msg]} {
          542  +            # Out of memory error, as expected
          543  +            integrity_check "malloc3-(integrity).$iterid"
          544  +            incr iFail
          545  +            if {$nac && !$ac} {
          546  +              if {![lindex $v 0]} {
          547  +                error "Statement \"[lindex $v 1]\" caused a rollback"
          548  +              }
          549  +# puts "Statement \"[lindex $v 1]\" caused a rollback"
          550  +              for {set i $begin_pc} {$i < $pc} {incr i} {
          551  +                set k2 [lindex $arglist [expr 2 * $i]]
          552  +                set v2 [lindex $arglist [expr 2 * $i + 1]]
          553  +                set catchupsql ""
          554  +                switch -- $k2 {
          555  +                  -sql  {set catchupsql [lindex $v2 1]}
          556  +                  -prep {set catchupsql $v2}
          557  +                }
          558  +# puts "CATCHUP $iterid $i $catchupsql"
          559  +                db eval $catchupsql
          560  +              }
          561  +            }
          562  +        } else {
          563  +            error $msg
          564  +        }
          565  +
          566  +        while {[lindex $arglist [expr 2 * ($pc -1)]] == "-test"} {
          567  +          incr pc -1
          568  +        }
          569  +      }
          570  +
          571  +      -prep {
          572  +# puts "PREP $iterid $v"
          573  +        db eval $v
          574  +        incr pc
          575  +      }
          576  +
          577  +      default { error "Unknown switch: $k" }
          578  +    }
          579  +  }
          580  +}
          581  +
          582  +# Turn of the Tcl interface's prepared statement caching facility.
          583  +db cache size 0
          584  +
          585  +#run_test $::run_test_script 59 97 
          586  +run_test $::run_test_script
          587  +sqlite_malloc_fail 0
          588  +finish_test
          589  +