blob: 902f683104db1532013db09b579636b718f7f8f2 [file] [log] [blame]
drh5a3032b2007-09-03 16:12:09 +00001# 2007 May 05
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# This file contains common code used by many different malloc tests
13# within the test suite.
14#
danielk197798c21902008-09-23 16:41:29 +000015# $Id: malloc_common.tcl,v 1.22 2008/09/23 16:41:30 danielk1977 Exp $
danielk1977c9cf9012007-05-30 10:36:47 +000016
drh5a3032b2007-09-03 16:12:09 +000017# If we did not compile with malloc testing enabled, then do nothing.
18#
drh3088d592008-03-21 16:45:47 +000019ifcapable builtin_test {
drh5efaf072008-03-18 00:07:10 +000020 set MEMDEBUG 1
21} else {
drheee4c8c2008-02-18 22:24:57 +000022 set MEMDEBUG 0
danielk1977369ff422007-09-03 07:31:09 +000023 return 0
danielk1977cdc3a6b2007-08-25 13:09:26 +000024}
25
dan1f55e282010-06-03 09:25:10 +000026# Transient and persistent OOM errors:
27#
28set FAULTSIM(oom-transient) [list \
29 -injectstart {oom_injectstart 0} \
30 -injectstop oom_injectstop \
31 -injecterrlist {{1 {out of memory}}} \
32]
33set FAULTSIM(oom-persistent) [list \
34 -injectstart {oom_injectstart 1000000} \
35 -injectstop oom_injectstop \
36 -injecterrlist {{1 {out of memory}}} \
37]
38
39# Transient and persistent IO errors:
40#
41set FAULTSIM(ioerr-transient) [list \
42 -injectstart {ioerr_injectstart 0} \
43 -injectstop ioerr_injectstop \
44 -injecterrlist {{1 {disk I/O error}}} \
45]
46set FAULTSIM(ioerr-persistent) [list \
47 -injectstart {ioerr_injectstart 1} \
48 -injectstop ioerr_injectstop \
49 -injecterrlist {{1 {disk I/O error}}} \
50]
51
52# Transient and persistent SHM errors:
53#
54set FAULTSIM(shmerr-transient) [list \
55 -injectinstall shmerr_injectinstall \
56 -injectstart {shmerr_injectstart 0} \
57 -injectstop shmerr_injectstop \
58 -injecterrlist {{1 {disk I/O error}}} \
59 -injectuninstall shmerr_injectuninstall \
60]
61set FAULTSIM(shmerr-persistent) [list \
62 -injectinstall shmerr_injectinstall \
63 -injectstart {shmerr_injectstart 1} \
64 -injectstop shmerr_injectstop \
65 -injecterrlist {{1 {disk I/O error}}} \
66 -injectuninstall shmerr_injectuninstall \
67]
68
69
dana4bc1232010-06-01 17:46:38 +000070
71#--------------------------------------------------------------------------
72# Usage do_faultsim_test NAME ?OPTIONS...?
73#
74# -faults List of fault types to simulate.
75#
76# -prep Script to execute before -body.
77#
78# -body Script to execute (with fault injection).
79#
80# -test Script to execute after -body.
81#
82proc do_faultsim_test {name args} {
dan1f55e282010-06-03 09:25:10 +000083 global FAULTSIM
84
85 set DEFAULT(-faults) [array names FAULTSIM]
dana4bc1232010-06-01 17:46:38 +000086 set DEFAULT(-prep) ""
87 set DEFAULT(-body) ""
88 set DEFAULT(-test) ""
89
90 array set O [array get DEFAULT]
91 array set O $args
92 foreach o [array names O] {
93 if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" }
94 }
95
dan1f55e282010-06-03 09:25:10 +000096 set faultlist [list]
dana4bc1232010-06-01 17:46:38 +000097 foreach f $O(-faults) {
dan1f55e282010-06-03 09:25:10 +000098 set flist [array names FAULTSIM $f]
99 if {[llength $flist]==0} { error "unknown fault: $f" }
100 set faultlist [concat $faultlist $flist]
dana4bc1232010-06-01 17:46:38 +0000101 }
dan1f55e282010-06-03 09:25:10 +0000102
dana4bc1232010-06-01 17:46:38 +0000103 set testspec [list -prep $O(-prep) -body $O(-body) -test $O(-test)]
dan1f55e282010-06-03 09:25:10 +0000104 foreach f [lsort -unique $faultlist] {
105 eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec
dana4bc1232010-06-01 17:46:38 +0000106 }
107}
108
109#-------------------------------------------------------------------------
110# Procedures to save and restore the current file-system state:
111#
112# faultsim_save_and_close
113# faultsim_restore_and_reopen
114#
115proc faultsim_save_and_close {} {
116 foreach {a => b} {
117 test.db => testX.db
118 test.db-wal => testX.db-wal
119 test.db-journal => testX.db-journal
120 } {
121 if {[file exists $a]} {
122 file copy -force $a $b
123 } else {
124 file delete -force $b
125 }
126 }
127 catch { db close }
128 return ""
129}
130proc faultsim_restore_and_reopen {} {
131 catch { db close }
132 foreach {a => b} {
133 testX.db => test.db
134 testX.db-wal => test.db-wal
135 testX.db-journal => test.db-journal
136 } {
137 if {[file exists $a]} {
138 file copy -force $a $b
139 } else {
140 file delete -force $b
141 }
142 }
143 sqlite3 db test.db
144 sqlite3_extended_result_codes db 1
145 sqlite3_db_config_lookaside db 0 0 0
146}
147
dan1f55e282010-06-03 09:25:10 +0000148proc faultsim_integrity_check {{db db}} {
149 set ic [$db eval { PRAGMA integrity_check }]
150 if {$ic != "ok"} { error "Integrity check: $ic" }
151}
dana4bc1232010-06-01 17:46:38 +0000152
dan1f55e282010-06-03 09:25:10 +0000153proc faultsim_delete_and_reopen {{file test.db}} {
154 catch { db close }
155 file delete -force test.db test.db-wal test.db-journal
156 sqlite3 db test.db
157}
158
159
160# The following procs are used as [do_one_faultsim_test] callbacks when
161# injecting OOM faults into test cases.
dan8521abf2010-05-31 06:38:34 +0000162#
163proc oom_injectstart {nRepeat iFail} {
164 sqlite3_memdebug_fail $iFail -repeat $nRepeat
165}
166proc oom_injectstop {} {
167 sqlite3_memdebug_fail -1
168}
169
dan1f55e282010-06-03 09:25:10 +0000170# The following procs are used as [do_one_faultsim_test] callbacks when
171# injecting IO error faults into test cases.
172#
dana4bc1232010-06-01 17:46:38 +0000173proc ioerr_injectstart {persist iFail} {
174 set ::sqlite_io_error_persist $persist
175 set ::sqlite_io_error_pending $iFail
176}
177proc ioerr_injectstop {} {
178 set sv $::sqlite_io_error_hit
179 set ::sqlite_io_error_persist 0
180 set ::sqlite_io_error_pending 0
181 set ::sqlite_io_error_hardhit 0
182 set ::sqlite_io_error_hit 0
183 set ::sqlite_io_error_pending 0
184 return $sv
185}
186
dan1f55e282010-06-03 09:25:10 +0000187# The following procs are used as [do_one_faultsim_test] callbacks when
188# injecting shared-memory related error faults into test cases.
189#
190proc shmerr_injectinstall {} {
191 testvfs shmfault -default true
192}
193proc shmerr_injectuninstall {} {
194 catch {db close}
195 catch {db2 close}
196 shmfault delete
197}
198proc shmerr_injectstart {persist iFail} {
199 shmfault ioerr $iFail $persist
200}
201proc shmerr_injectstop {} {
202 shmfault ioerr 0 0
203}
204
dana4bc1232010-06-01 17:46:38 +0000205# This command is not called directly. It is used by the
206# [faultsim_test_result] command created by [do_faultsim_test] and used
207# by -test scripts.
dan8521abf2010-05-31 06:38:34 +0000208#
dana4bc1232010-06-01 17:46:38 +0000209proc faultsim_test_result_int {args} {
dan8521abf2010-05-31 06:38:34 +0000210 upvar testrc testrc testresult testresult testnfail testnfail
211 set t [list $testrc $testresult]
dana4bc1232010-06-01 17:46:38 +0000212 set r $args
dan8521abf2010-05-31 06:38:34 +0000213 if { ($testnfail==0 && $t != [lindex $r 0]) || [lsearch $r $t]<0 } {
214 error "nfail=$testnfail rc=$testrc result=$testresult"
215 }
216}
217
dana4bc1232010-06-01 17:46:38 +0000218#--------------------------------------------------------------------------
219# Usage do_one_faultsim_test NAME ?OPTIONS...?
dan8521abf2010-05-31 06:38:34 +0000220#
221# The first argument, <test number>, is used as a prefix of the test names
222# taken by tests executed by this command. Options are as follows. All
223# options take a single argument.
224#
225# -injectstart Script to enable fault-injection.
226#
227# -injectstop Script to disable fault-injection.
228#
dana4bc1232010-06-01 17:46:38 +0000229# -injecterrlist List of generally acceptable test results (i.e. error
230# messages). Example: [list {1 {out of memory}}]
231#
dan1f55e282010-06-03 09:25:10 +0000232# -injectinstall
233#
234# -injectuninstall
235#
dan8521abf2010-05-31 06:38:34 +0000236# -prep Script to execute before -body.
237#
238# -body Script to execute (with fault injection).
239#
240# -test Script to execute after -body.
241#
dana4bc1232010-06-01 17:46:38 +0000242proc do_one_faultsim_test {testname args} {
dan8521abf2010-05-31 06:38:34 +0000243
dan1f55e282010-06-03 09:25:10 +0000244 set DEFAULT(-injectstart) "expr"
245 set DEFAULT(-injectstop) "expr 0"
246 set DEFAULT(-injecterrlist) [list]
247 set DEFAULT(-injectinstall) ""
248 set DEFAULT(-injectuninstall) ""
249 set DEFAULT(-prep) ""
250 set DEFAULT(-body) ""
251 set DEFAULT(-test) ""
dan8521abf2010-05-31 06:38:34 +0000252
253 array set O [array get DEFAULT]
254 array set O $args
255 foreach o [array names O] {
256 if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" }
257 }
258
259 proc faultsim_test_proc {testrc testresult testnfail} $O(-test)
dana4bc1232010-06-01 17:46:38 +0000260 proc faultsim_test_result {args} "
261 uplevel faultsim_test_result_int \$args [list $O(-injecterrlist)]
262 "
dan8521abf2010-05-31 06:38:34 +0000263
dan1f55e282010-06-03 09:25:10 +0000264 eval $O(-injectinstall)
265
dan8521abf2010-05-31 06:38:34 +0000266 set stop 0
267 for {set iFail 1} {!$stop} {incr iFail} {
268
269 # Evaluate the -prep script.
270 #
271 eval $O(-prep)
272
273 # Start the fault-injection. Run the -body script. Stop the fault
274 # injection. Local var $nfail is set to the total number of faults
275 # injected into the system this trial.
276 #
277 eval $O(-injectstart) $iFail
278 set rc [catch $O(-body) res]
279 set nfail [eval $O(-injectstop)]
280
281 # Run the -test script. If it throws no error, consider this trial
282 # sucessful. If it does throw an error, cause a [do_test] test to
283 # fail (and print out the unexpected exception thrown by the -test
284 # script at the same time).
285 #
286 set rc [catch [list faultsim_test_proc $rc $res $nfail] res]
287 if {$rc == 0} {set res ok}
288 do_test $testname.$iFail [list list $rc $res] {0 ok}
289
290 # If no faults where injected this trial, don't bother running
291 # any more. This test is finished.
292 #
293 if {$nfail==0} { set stop 1 }
294 }
dan1f55e282010-06-03 09:25:10 +0000295
296 eval $O(-injectuninstall)
dan8521abf2010-05-31 06:38:34 +0000297}
298
danielk1977c9cf9012007-05-30 10:36:47 +0000299# Usage: do_malloc_test <test number> <options...>
300#
301# The first argument, <test number>, is an integer used to name the
302# tests executed by this proc. Options are as follows:
303#
304# -tclprep TCL script to run to prepare test.
305# -sqlprep SQL script to run to prepare test.
306# -tclbody TCL script to run with malloc failure simulation.
307# -sqlbody TCL script to run with malloc failure simulation.
308# -cleanup TCL script to run after the test.
309#
310# This command runs a series of tests to verify SQLite's ability
311# to handle an out-of-memory condition gracefully. It is assumed
312# that if this condition occurs a malloc() call will return a
313# NULL pointer. Linux, for example, doesn't do that by default. See
314# the "BUGS" section of malloc(3).
315#
316# Each iteration of a loop, the TCL commands in any argument passed
317# to the -tclbody switch, followed by the SQL commands in any argument
318# passed to the -sqlbody switch are executed. Each iteration the
319# Nth call to sqliteMalloc() is made to fail, where N is increased
320# each time the loop runs starting from 1. When all commands execute
321# successfully, the loop ends.
322#
323proc do_malloc_test {tn args} {
324 array unset ::mallocopts
325 array set ::mallocopts $args
326
327 if {[string is integer $tn]} {
328 set tn malloc-$tn
329 }
drhf3a65f72007-08-22 20:18:21 +0000330 if {[info exists ::mallocopts(-start)]} {
331 set start $::mallocopts(-start)
332 } else {
danielk1977ae72d982007-10-03 08:46:44 +0000333 set start 0
drhf3a65f72007-08-22 20:18:21 +0000334 }
drh1527ff42008-01-18 17:03:32 +0000335 if {[info exists ::mallocopts(-end)]} {
336 set end $::mallocopts(-end)
337 } else {
338 set end 50000
339 }
drh93aed5a2008-01-16 17:46:38 +0000340 save_prng_state
danielk1977c9cf9012007-05-30 10:36:47 +0000341
drh643167f2008-01-22 21:30:53 +0000342 foreach ::iRepeat {0 10000000} {
danielk1977a1644fd2007-08-29 12:31:25 +0000343 set ::go 1
drh1527ff42008-01-18 17:03:32 +0000344 for {set ::n $start} {$::go && $::n <= $end} {incr ::n} {
danielk1977c9cf9012007-05-30 10:36:47 +0000345
danielk1977a1644fd2007-08-29 12:31:25 +0000346 # If $::iRepeat is 0, then the malloc() failure is transient - it
347 # fails and then subsequent calls succeed. If $::iRepeat is 1,
348 # then the failure is persistent - once malloc() fails it keeps
349 # failing.
danielk1977c9cf9012007-05-30 10:36:47 +0000350 #
danielk1977a1644fd2007-08-29 12:31:25 +0000351 set zRepeat "transient"
352 if {$::iRepeat} {set zRepeat "persistent"}
drh93aed5a2008-01-16 17:46:38 +0000353 restore_prng_state
354 foreach file [glob -nocomplain test.db-mj*] {file delete -force $file}
danielk1977c9cf9012007-05-30 10:36:47 +0000355
danielk1977a1644fd2007-08-29 12:31:25 +0000356 do_test ${tn}.${zRepeat}.${::n} {
357
358 # Remove all traces of database files test.db and test2.db
359 # from the file-system. Then open (empty database) "test.db"
360 # with the handle [db].
361 #
362 catch {db close}
363 catch {file delete -force test.db}
364 catch {file delete -force test.db-journal}
dan5e9e4822010-05-03 18:01:21 +0000365 catch {file delete -force test.db-wal}
danielk1977a1644fd2007-08-29 12:31:25 +0000366 catch {file delete -force test2.db}
367 catch {file delete -force test2.db-journal}
dan5e9e4822010-05-03 18:01:21 +0000368 catch {file delete -force test2.db-wal}
danielk1977a1644fd2007-08-29 12:31:25 +0000369 if {[info exists ::mallocopts(-testdb)]} {
370 file copy $::mallocopts(-testdb) test.db
danielk1977c9cf9012007-05-30 10:36:47 +0000371 }
danielk1977ae72d982007-10-03 08:46:44 +0000372 catch { sqlite3 db test.db }
373 if {[info commands db] ne ""} {
374 sqlite3_extended_result_codes db 1
375 }
drhe9d1c722008-08-04 20:13:26 +0000376 sqlite3_db_config_lookaside db 0 0 0
danielk1977a1644fd2007-08-29 12:31:25 +0000377
378 # Execute any -tclprep and -sqlprep scripts.
379 #
380 if {[info exists ::mallocopts(-tclprep)]} {
381 eval $::mallocopts(-tclprep)
382 }
383 if {[info exists ::mallocopts(-sqlprep)]} {
384 execsql $::mallocopts(-sqlprep)
385 }
386
387 # Now set the ${::n}th malloc() to fail and execute the -tclbody
388 # and -sqlbody scripts.
389 #
390 sqlite3_memdebug_fail $::n -repeat $::iRepeat
391 set ::mallocbody {}
392 if {[info exists ::mallocopts(-tclbody)]} {
393 append ::mallocbody "$::mallocopts(-tclbody)\n"
394 }
395 if {[info exists ::mallocopts(-sqlbody)]} {
396 append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
397 }
danielk1977c9cf9012007-05-30 10:36:47 +0000398
danielk1977a1644fd2007-08-29 12:31:25 +0000399 # The following block sets local variables as follows:
400 #
401 # isFail - True if an error (any error) was reported by sqlite.
402 # nFail - The total number of simulated malloc() failures.
403 # nBenign - The number of benign simulated malloc() failures.
404 #
405 set isFail [catch $::mallocbody msg]
406 set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
drh643167f2008-01-22 21:30:53 +0000407 # puts -nonewline " (isFail=$isFail nFail=$nFail nBenign=$nBenign) "
danielk1977a1644fd2007-08-29 12:31:25 +0000408
409 # If one or more mallocs failed, run this loop body again.
410 #
411 set go [expr {$nFail>0}]
412
413 if {($nFail-$nBenign)==0} {
414 if {$isFail} {
415 set v2 $msg
416 } else {
417 set isFail 1
418 set v2 1
419 }
420 } elseif {!$isFail} {
421 set v2 $msg
danielk1977ae72d982007-10-03 08:46:44 +0000422 } elseif {
423 [info command db]=="" ||
424 [db errorcode]==7 ||
danielk1977ae72d982007-10-03 08:46:44 +0000425 $msg=="out of memory"
426 } {
danielk1977a1644fd2007-08-29 12:31:25 +0000427 set v2 1
428 } else {
429 set v2 $msg
danielk1977ae72d982007-10-03 08:46:44 +0000430 puts [db errorcode]
danielk1977a1644fd2007-08-29 12:31:25 +0000431 }
432 lappend isFail $v2
433 } {1 1}
434
435 if {[info exists ::mallocopts(-cleanup)]} {
436 catch [list uplevel #0 $::mallocopts(-cleanup)] msg
437 }
danielk1977c9cf9012007-05-30 10:36:47 +0000438 }
439 }
440 unset ::mallocopts
danielk197777519402007-08-30 11:48:31 +0000441 sqlite3_memdebug_fail -1
danielk1977c9cf9012007-05-30 10:36:47 +0000442}
danef378022010-05-04 11:06:03 +0000443
444
445#-------------------------------------------------------------------------
446# This proc is used to test a single SELECT statement. Parameter $name is
447# passed a name for the test case (i.e. "fts3_malloc-1.4.1") and parameter
448# $sql is passed the text of the SELECT statement. Parameter $result is
449# set to the expected output if the SELECT statement is successfully
450# executed using [db eval].
451#
452# Example:
453#
454# do_select_test testcase-1.1 "SELECT 1+1, 1+2" {1 2}
455#
456# If global variable DO_MALLOC_TEST is set to a non-zero value, or if
457# it is not defined at all, then OOM testing is performed on the SELECT
458# statement. Each OOM test case is said to pass if either (a) executing
459# the SELECT statement succeeds and the results match those specified
460# by parameter $result, or (b) TCL throws an "out of memory" error.
461#
462# If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement
463# is executed just once. In this case the test case passes if the results
464# match the expected results passed via parameter $result.
465#
466proc do_select_test {name sql result} {
467 uplevel [list doPassiveTest 0 $name $sql [list 0 $result]]
468}
469
470proc do_restart_select_test {name sql result} {
471 uplevel [list doPassiveTest 1 $name $sql [list 0 $result]]
472}
473
474proc do_error_test {name sql error} {
475 uplevel [list doPassiveTest 0 $name $sql [list 1 $error]]
476}
477
478proc doPassiveTest {isRestart name sql catchres} {
479 if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
480
481 switch $::DO_MALLOC_TEST {
482 0 { # No malloc failures.
483 do_test $name [list set {} [uplevel [list catchsql $sql]]] $catchres
484 return
485 }
486 1 { # Simulate transient failures.
487 set nRepeat 1
488 set zName "transient"
489 set nStartLimit 100000
490 set nBackup 1
491 }
492 2 { # Simulate persistent failures.
493 set nRepeat 1
494 set zName "persistent"
495 set nStartLimit 100000
496 set nBackup 1
497 }
498 3 { # Simulate transient failures with extra brute force.
499 set nRepeat 100000
500 set zName "ridiculous"
501 set nStartLimit 1
502 set nBackup 10
503 }
504 }
505
506 # The set of acceptable results from running [catchsql $sql].
507 #
508 set answers [list {1 {out of memory}} $catchres]
509 set str [join $answers " OR "]
510
511 set nFail 1
512 for {set iLimit $nStartLimit} {$nFail} {incr iLimit} {
513 for {set iFail 1} {$nFail && $iFail<=$iLimit} {incr iFail} {
514 for {set iTest 0} {$iTest<$nBackup && ($iFail-$iTest)>0} {incr iTest} {
515
516 if {$isRestart} { sqlite3 db test.db }
517
518 sqlite3_memdebug_fail [expr $iFail-$iTest] -repeat $nRepeat
519 set res [uplevel [list catchsql $sql]]
520 if {[lsearch -exact $answers $res]>=0} { set res $str }
521 set testname "$name.$zName.$iFail"
522 do_test "$name.$zName.$iLimit.$iFail" [list set {} $res] $str
523
524 set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
525 }
526 }
527 }
528}
529
530
531#-------------------------------------------------------------------------
532# Test a single write to the database. In this case a "write" is a
533# DELETE, UPDATE or INSERT statement.
534#
535# If OOM testing is performed, there are several acceptable outcomes:
536#
537# 1) The write succeeds. No error is returned.
538#
539# 2) An "out of memory" exception is thrown and:
540#
541# a) The statement has no effect, OR
542# b) The current transaction is rolled back, OR
543# c) The statement succeeds. This can only happen if the connection
544# is in auto-commit mode (after the statement is executed, so this
545# includes COMMIT statements).
546#
547# If the write operation eventually succeeds, zero is returned. If a
548# transaction is rolled back, non-zero is returned.
549#
550# Parameter $name is the name to use for the test case (or test cases).
551# The second parameter, $tbl, should be the name of the database table
552# being modified. Parameter $sql contains the SQL statement to test.
553#
554proc do_write_test {name tbl sql} {
555 if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
556
557 # Figure out an statement to get a checksum for table $tbl.
558 db eval "SELECT * FROM $tbl" V break
559 set cksumsql "SELECT md5sum([join [concat rowid $V(*)] ,]) FROM $tbl"
560
561 # Calculate the initial table checksum.
562 set cksum1 [db one $cksumsql]
563
564 if {$::DO_MALLOC_TEST } {
565 set answers [list {1 {out of memory}} {0 {}}]
566 if {$::DO_MALLOC_TEST==1} {
567 set modes {100000 transient}
568 } else {
569 set modes {1 persistent}
570 }
571 } else {
572 set answers [list {0 {}}]
573 set modes [list 0 nofail]
574 }
575 set str [join $answers " OR "]
576
577 foreach {nRepeat zName} $modes {
578 for {set iFail 1} 1 {incr iFail} {
579 if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat}
580
581 set res [uplevel [list catchsql $sql]]
582 set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
583 if {$nFail==0} {
584 do_test $name.$zName.$iFail [list set {} $res] {0 {}}
585 return
586 } else {
587 if {[lsearch $answers $res]>=0} {
588 set res $str
589 }
590 do_test $name.$zName.$iFail [list set {} $res] $str
591 set cksum2 [db one $cksumsql]
592 if {$cksum1 != $cksum2} return
593 }
594 }
595 }
596}