malloc_common.tcl 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. # 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. #
  15. # $Id: malloc_common.tcl,v 1.22 2008/09/23 16:41:30 danielk1977 Exp $
  16. # If we did not compile with malloc testing enabled, then do nothing.
  17. #
  18. ifcapable builtin_test {
  19. set MEMDEBUG 1
  20. } else {
  21. set MEMDEBUG 0
  22. return 0
  23. }
  24. # Transient and persistent OOM errors:
  25. #
  26. set FAULTSIM(oom-transient) [list \
  27. -injectstart {oom_injectstart 0} \
  28. -injectstop oom_injectstop \
  29. -injecterrlist {{1 {out of memory}}} \
  30. ]
  31. set FAULTSIM(oom-persistent) [list \
  32. -injectstart {oom_injectstart 1000000} \
  33. -injectstop oom_injectstop \
  34. -injecterrlist {{1 {out of memory}}} \
  35. ]
  36. # Transient and persistent IO errors:
  37. #
  38. set FAULTSIM(ioerr-transient) [list \
  39. -injectstart {ioerr_injectstart 0} \
  40. -injectstop ioerr_injectstop \
  41. -injecterrlist {{1 {disk I/O error}}} \
  42. ]
  43. set FAULTSIM(ioerr-persistent) [list \
  44. -injectstart {ioerr_injectstart 1} \
  45. -injectstop ioerr_injectstop \
  46. -injecterrlist {{1 {disk I/O error}}} \
  47. ]
  48. # SQLITE_FULL errors (always persistent):
  49. #
  50. set FAULTSIM(full) [list \
  51. -injectinstall fullerr_injectinstall \
  52. -injectstart fullerr_injectstart \
  53. -injectstop fullerr_injectstop \
  54. -injecterrlist {{1 {database or disk is full}}} \
  55. -injectuninstall fullerr_injectuninstall \
  56. ]
  57. # Transient and persistent SHM errors:
  58. #
  59. set FAULTSIM(shmerr-transient) [list \
  60. -injectinstall shmerr_injectinstall \
  61. -injectstart {shmerr_injectstart 0} \
  62. -injectstop shmerr_injectstop \
  63. -injecterrlist {{1 {disk I/O error}}} \
  64. -injectuninstall shmerr_injectuninstall \
  65. ]
  66. set FAULTSIM(shmerr-persistent) [list \
  67. -injectinstall shmerr_injectinstall \
  68. -injectstart {shmerr_injectstart 1} \
  69. -injectstop shmerr_injectstop \
  70. -injecterrlist {{1 {disk I/O error}}} \
  71. -injectuninstall shmerr_injectuninstall \
  72. ]
  73. # Transient and persistent CANTOPEN errors:
  74. #
  75. set FAULTSIM(cantopen-transient) [list \
  76. -injectinstall cantopen_injectinstall \
  77. -injectstart {cantopen_injectstart 0} \
  78. -injectstop cantopen_injectstop \
  79. -injecterrlist {{1 {unable to open database file}}} \
  80. -injectuninstall cantopen_injectuninstall \
  81. ]
  82. set FAULTSIM(cantopen-persistent) [list \
  83. -injectinstall cantopen_injectinstall \
  84. -injectstart {cantopen_injectstart 1} \
  85. -injectstop cantopen_injectstop \
  86. -injecterrlist {{1 {unable to open database file}}} \
  87. -injectuninstall cantopen_injectuninstall \
  88. ]
  89. set FAULTSIM(interrupt) [list \
  90. -injectinstall interrupt_injectinstall \
  91. -injectstart interrupt_injectstart \
  92. -injectstop interrupt_injectstop \
  93. -injecterrlist {{1 interrupted} {1 interrupt}} \
  94. -injectuninstall interrupt_injectuninstall \
  95. ]
  96. #--------------------------------------------------------------------------
  97. # Usage do_faultsim_test NAME ?OPTIONS...?
  98. #
  99. # -faults List of fault types to simulate.
  100. #
  101. # -prep Script to execute before -body.
  102. #
  103. # -body Script to execute (with fault injection).
  104. #
  105. # -test Script to execute after -body.
  106. #
  107. # -install Script to execute after faultsim -injectinstall
  108. #
  109. # -uninstall Script to execute after faultsim -uninjectinstall
  110. #
  111. proc do_faultsim_test {name args} {
  112. global FAULTSIM
  113. foreach n [array names FAULTSIM] {
  114. if {$n != "interrupt"} {lappend DEFAULT(-faults) $n}
  115. }
  116. set DEFAULT(-prep) ""
  117. set DEFAULT(-body) ""
  118. set DEFAULT(-test) ""
  119. set DEFAULT(-install) ""
  120. set DEFAULT(-uninstall) ""
  121. fix_testname name
  122. array set O [array get DEFAULT]
  123. array set O $args
  124. foreach o [array names O] {
  125. if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" }
  126. }
  127. set faultlist [list]
  128. foreach f $O(-faults) {
  129. set flist [array names FAULTSIM $f]
  130. if {[llength $flist]==0} { error "unknown fault: $f" }
  131. set faultlist [concat $faultlist $flist]
  132. }
  133. set testspec [list -prep $O(-prep) -body $O(-body) \
  134. -test $O(-test) -install $O(-install) -uninstall $O(-uninstall)
  135. ]
  136. foreach f [lsort -unique $faultlist] {
  137. eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec
  138. }
  139. }
  140. #-------------------------------------------------------------------------
  141. # Procedures to save and restore the current file-system state:
  142. #
  143. # faultsim_save
  144. # faultsim_restore
  145. # faultsim_save_and_close
  146. # faultsim_restore_and_reopen
  147. # faultsim_delete_and_reopen
  148. #
  149. proc faultsim_save {args} { uplevel db_save $args }
  150. proc faultsim_save_and_close {args} { uplevel db_save_and_close $args }
  151. proc faultsim_restore {args} { uplevel db_restore $args }
  152. proc faultsim_restore_and_reopen {args} {
  153. uplevel db_restore_and_reopen $args
  154. sqlite3_extended_result_codes db 1
  155. sqlite3_db_config_lookaside db 0 0 0
  156. }
  157. proc faultsim_delete_and_reopen {args} {
  158. uplevel db_delete_and_reopen $args
  159. sqlite3_extended_result_codes db 1
  160. sqlite3_db_config_lookaside db 0 0 0
  161. }
  162. proc faultsim_integrity_check {{db db}} {
  163. set ic [$db eval { PRAGMA integrity_check }]
  164. if {$ic != "ok"} { error "Integrity check: $ic" }
  165. }
  166. # The following procs are used as [do_one_faultsim_test] callbacks when
  167. # injecting OOM faults into test cases.
  168. #
  169. proc oom_injectstart {nRepeat iFail} {
  170. sqlite3_memdebug_fail [expr $iFail-1] -repeat $nRepeat
  171. }
  172. proc oom_injectstop {} {
  173. sqlite3_memdebug_fail -1
  174. }
  175. # The following procs are used as [do_one_faultsim_test] callbacks when
  176. # injecting IO error faults into test cases.
  177. #
  178. proc ioerr_injectstart {persist iFail} {
  179. set ::sqlite_io_error_persist $persist
  180. set ::sqlite_io_error_pending $iFail
  181. }
  182. proc ioerr_injectstop {} {
  183. set sv $::sqlite_io_error_hit
  184. set ::sqlite_io_error_persist 0
  185. set ::sqlite_io_error_pending 0
  186. set ::sqlite_io_error_hardhit 0
  187. set ::sqlite_io_error_hit 0
  188. set ::sqlite_io_error_pending 0
  189. return $sv
  190. }
  191. # The following procs are used as [do_one_faultsim_test] callbacks when
  192. # injecting shared-memory related error faults into test cases.
  193. #
  194. proc shmerr_injectinstall {} {
  195. testvfs shmfault -default true
  196. shmfault filter {xShmOpen xShmMap xShmLock}
  197. }
  198. proc shmerr_injectuninstall {} {
  199. catch {db close}
  200. catch {db2 close}
  201. shmfault delete
  202. }
  203. proc shmerr_injectstart {persist iFail} {
  204. shmfault ioerr $iFail $persist
  205. }
  206. proc shmerr_injectstop {} {
  207. shmfault ioerr
  208. }
  209. # The following procs are used as [do_one_faultsim_test] callbacks when
  210. # injecting SQLITE_FULL error faults into test cases.
  211. #
  212. proc fullerr_injectinstall {} {
  213. testvfs shmfault -default true
  214. }
  215. proc fullerr_injectuninstall {} {
  216. catch {db close}
  217. catch {db2 close}
  218. shmfault delete
  219. }
  220. proc fullerr_injectstart {iFail} {
  221. shmfault full $iFail 1
  222. }
  223. proc fullerr_injectstop {} {
  224. shmfault full
  225. }
  226. # The following procs are used as [do_one_faultsim_test] callbacks when
  227. # injecting SQLITE_CANTOPEN error faults into test cases.
  228. #
  229. proc cantopen_injectinstall {} {
  230. testvfs shmfault -default true
  231. }
  232. proc cantopen_injectuninstall {} {
  233. catch {db close}
  234. catch {db2 close}
  235. shmfault delete
  236. }
  237. proc cantopen_injectstart {persist iFail} {
  238. shmfault cantopen $iFail $persist
  239. }
  240. proc cantopen_injectstop {} {
  241. shmfault cantopen
  242. }
  243. # The following procs are used as [do_one_faultsim_test] callbacks
  244. # when injecting SQLITE_INTERRUPT error faults into test cases.
  245. #
  246. proc interrupt_injectinstall {} {
  247. }
  248. proc interrupt_injectuninstall {} {
  249. }
  250. proc interrupt_injectstart {iFail} {
  251. set ::sqlite_interrupt_count $iFail
  252. }
  253. proc interrupt_injectstop {} {
  254. set res [expr $::sqlite_interrupt_count<=0]
  255. set ::sqlite_interrupt_count 0
  256. set res
  257. }
  258. # This command is not called directly. It is used by the
  259. # [faultsim_test_result] command created by [do_faultsim_test] and used
  260. # by -test scripts.
  261. #
  262. proc faultsim_test_result_int {args} {
  263. upvar testrc testrc testresult testresult testnfail testnfail
  264. set t [list $testrc $testresult]
  265. set r $args
  266. if { ($testnfail==0 && $t != [lindex $r 0]) || [lsearch $r $t]<0 } {
  267. error "nfail=$testnfail rc=$testrc result=$testresult list=$r"
  268. }
  269. }
  270. #--------------------------------------------------------------------------
  271. # Usage do_one_faultsim_test NAME ?OPTIONS...?
  272. #
  273. # The first argument, <test number>, is used as a prefix of the test names
  274. # taken by tests executed by this command. Options are as follows. All
  275. # options take a single argument.
  276. #
  277. # -injectstart Script to enable fault-injection.
  278. #
  279. # -injectstop Script to disable fault-injection.
  280. #
  281. # -injecterrlist List of generally acceptable test results (i.e. error
  282. # messages). Example: [list {1 {out of memory}}]
  283. #
  284. # -injectinstall
  285. #
  286. # -injectuninstall
  287. #
  288. # -prep Script to execute before -body.
  289. #
  290. # -body Script to execute (with fault injection).
  291. #
  292. # -test Script to execute after -body.
  293. #
  294. proc do_one_faultsim_test {testname args} {
  295. set DEFAULT(-injectstart) "expr"
  296. set DEFAULT(-injectstop) "expr 0"
  297. set DEFAULT(-injecterrlist) [list]
  298. set DEFAULT(-injectinstall) ""
  299. set DEFAULT(-injectuninstall) ""
  300. set DEFAULT(-prep) ""
  301. set DEFAULT(-body) ""
  302. set DEFAULT(-test) ""
  303. set DEFAULT(-install) ""
  304. set DEFAULT(-uninstall) ""
  305. array set O [array get DEFAULT]
  306. array set O $args
  307. foreach o [array names O] {
  308. if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" }
  309. }
  310. proc faultsim_test_proc {testrc testresult testnfail} $O(-test)
  311. proc faultsim_test_result {args} "
  312. uplevel faultsim_test_result_int \$args [list $O(-injecterrlist)]
  313. "
  314. eval $O(-injectinstall)
  315. eval $O(-install)
  316. set stop 0
  317. for {set iFail 1} {!$stop} {incr iFail} {
  318. # Evaluate the -prep script.
  319. #
  320. eval $O(-prep)
  321. # Start the fault-injection. Run the -body script. Stop the fault
  322. # injection. Local var $nfail is set to the total number of faults
  323. # injected into the system this trial.
  324. #
  325. eval $O(-injectstart) $iFail
  326. set rc [catch $O(-body) res]
  327. set nfail [eval $O(-injectstop)]
  328. # Run the -test script. If it throws no error, consider this trial
  329. # sucessful. If it does throw an error, cause a [do_test] test to
  330. # fail (and print out the unexpected exception thrown by the -test
  331. # script at the same time).
  332. #
  333. set rc [catch [list faultsim_test_proc $rc $res $nfail] res]
  334. if {$rc == 0} {set res ok}
  335. do_test $testname.$iFail [list list $rc $res] {0 ok}
  336. # If no faults where injected this trial, don't bother running
  337. # any more. This test is finished.
  338. #
  339. if {$nfail==0} { set stop 1 }
  340. }
  341. eval $O(-uninstall)
  342. eval $O(-injectuninstall)
  343. }
  344. # Usage: do_malloc_test <test number> <options...>
  345. #
  346. # The first argument, <test number>, is an integer used to name the
  347. # tests executed by this proc. Options are as follows:
  348. #
  349. # -tclprep TCL script to run to prepare test.
  350. # -sqlprep SQL script to run to prepare test.
  351. # -tclbody TCL script to run with malloc failure simulation.
  352. # -sqlbody TCL script to run with malloc failure simulation.
  353. # -cleanup TCL script to run after the test.
  354. #
  355. # This command runs a series of tests to verify SQLite's ability
  356. # to handle an out-of-memory condition gracefully. It is assumed
  357. # that if this condition occurs a malloc() call will return a
  358. # NULL pointer. Linux, for example, doesn't do that by default. See
  359. # the "BUGS" section of malloc(3).
  360. #
  361. # Each iteration of a loop, the TCL commands in any argument passed
  362. # to the -tclbody switch, followed by the SQL commands in any argument
  363. # passed to the -sqlbody switch are executed. Each iteration the
  364. # Nth call to sqliteMalloc() is made to fail, where N is increased
  365. # each time the loop runs starting from 1. When all commands execute
  366. # successfully, the loop ends.
  367. #
  368. proc do_malloc_test {tn args} {
  369. array unset ::mallocopts
  370. array set ::mallocopts $args
  371. if {[string is integer $tn]} {
  372. set tn malloc-$tn
  373. }
  374. if {[info exists ::mallocopts(-start)]} {
  375. set start $::mallocopts(-start)
  376. } else {
  377. set start 0
  378. }
  379. if {[info exists ::mallocopts(-end)]} {
  380. set end $::mallocopts(-end)
  381. } else {
  382. set end 50000
  383. }
  384. save_prng_state
  385. foreach ::iRepeat {0 10000000} {
  386. set ::go 1
  387. for {set ::n $start} {$::go && $::n <= $end} {incr ::n} {
  388. # If $::iRepeat is 0, then the malloc() failure is transient - it
  389. # fails and then subsequent calls succeed. If $::iRepeat is 1,
  390. # then the failure is persistent - once malloc() fails it keeps
  391. # failing.
  392. #
  393. set zRepeat "transient"
  394. if {$::iRepeat} {set zRepeat "persistent"}
  395. restore_prng_state
  396. foreach file [glob -nocomplain test.db-mj*] {forcedelete $file}
  397. do_test ${tn}.${zRepeat}.${::n} {
  398. # Remove all traces of database files test.db and test2.db
  399. # from the file-system. Then open (empty database) "test.db"
  400. # with the handle [db].
  401. #
  402. catch {db close}
  403. catch {db2 close}
  404. forcedelete test.db
  405. forcedelete test.db-journal
  406. forcedelete test.db-wal
  407. forcedelete test2.db
  408. forcedelete test2.db-journal
  409. forcedelete test2.db-wal
  410. if {[info exists ::mallocopts(-testdb)]} {
  411. copy_file $::mallocopts(-testdb) test.db
  412. }
  413. catch { sqlite3 db test.db }
  414. if {[info commands db] ne ""} {
  415. sqlite3_extended_result_codes db 1
  416. }
  417. sqlite3_db_config_lookaside db 0 0 0
  418. # Execute any -tclprep and -sqlprep scripts.
  419. #
  420. if {[info exists ::mallocopts(-tclprep)]} {
  421. eval $::mallocopts(-tclprep)
  422. }
  423. if {[info exists ::mallocopts(-sqlprep)]} {
  424. execsql $::mallocopts(-sqlprep)
  425. }
  426. # Now set the ${::n}th malloc() to fail and execute the -tclbody
  427. # and -sqlbody scripts.
  428. #
  429. sqlite3_memdebug_fail $::n -repeat $::iRepeat
  430. set ::mallocbody {}
  431. if {[info exists ::mallocopts(-tclbody)]} {
  432. append ::mallocbody "$::mallocopts(-tclbody)\n"
  433. }
  434. if {[info exists ::mallocopts(-sqlbody)]} {
  435. append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
  436. }
  437. # The following block sets local variables as follows:
  438. #
  439. # isFail - True if an error (any error) was reported by sqlite.
  440. # nFail - The total number of simulated malloc() failures.
  441. # nBenign - The number of benign simulated malloc() failures.
  442. #
  443. set isFail [catch $::mallocbody msg]
  444. set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
  445. # puts -nonewline " (isFail=$isFail nFail=$nFail nBenign=$nBenign) "
  446. # If one or more mallocs failed, run this loop body again.
  447. #
  448. set go [expr {$nFail>0}]
  449. if {($nFail-$nBenign)==0} {
  450. if {$isFail} {
  451. set v2 $msg
  452. } else {
  453. set isFail 1
  454. set v2 1
  455. }
  456. } elseif {!$isFail} {
  457. set v2 $msg
  458. } elseif {
  459. [info command db]=="" ||
  460. [db errorcode]==7 ||
  461. $msg=="out of memory"
  462. } {
  463. set v2 1
  464. } else {
  465. set v2 $msg
  466. puts [db errorcode]
  467. }
  468. lappend isFail $v2
  469. } {1 1}
  470. if {[info exists ::mallocopts(-cleanup)]} {
  471. catch [list uplevel #0 $::mallocopts(-cleanup)] msg
  472. }
  473. }
  474. }
  475. unset ::mallocopts
  476. sqlite3_memdebug_fail -1
  477. }
  478. #-------------------------------------------------------------------------
  479. # This proc is used to test a single SELECT statement. Parameter $name is
  480. # passed a name for the test case (i.e. "fts3_malloc-1.4.1") and parameter
  481. # $sql is passed the text of the SELECT statement. Parameter $result is
  482. # set to the expected output if the SELECT statement is successfully
  483. # executed using [db eval].
  484. #
  485. # Example:
  486. #
  487. # do_select_test testcase-1.1 "SELECT 1+1, 1+2" {1 2}
  488. #
  489. # If global variable DO_MALLOC_TEST is set to a non-zero value, or if
  490. # it is not defined at all, then OOM testing is performed on the SELECT
  491. # statement. Each OOM test case is said to pass if either (a) executing
  492. # the SELECT statement succeeds and the results match those specified
  493. # by parameter $result, or (b) TCL throws an "out of memory" error.
  494. #
  495. # If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement
  496. # is executed just once. In this case the test case passes if the results
  497. # match the expected results passed via parameter $result.
  498. #
  499. proc do_select_test {name sql result} {
  500. uplevel [list doPassiveTest 0 $name $sql [list 0 [list {*}$result]]]
  501. }
  502. proc do_restart_select_test {name sql result} {
  503. uplevel [list doPassiveTest 1 $name $sql [list 0 $result]]
  504. }
  505. proc do_error_test {name sql error} {
  506. uplevel [list doPassiveTest 0 $name $sql [list 1 $error]]
  507. }
  508. proc doPassiveTest {isRestart name sql catchres} {
  509. if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
  510. if {[info exists ::testprefix]
  511. && [string is integer [string range $name 0 0]]
  512. } {
  513. set name $::testprefix.$name
  514. }
  515. switch $::DO_MALLOC_TEST {
  516. 0 { # No malloc failures.
  517. do_test $name [list set {} [uplevel [list catchsql $sql]]] $catchres
  518. return
  519. }
  520. 1 { # Simulate transient failures.
  521. set nRepeat 1
  522. set zName "transient"
  523. set nStartLimit 100000
  524. set nBackup 1
  525. }
  526. 2 { # Simulate persistent failures.
  527. set nRepeat 1
  528. set zName "persistent"
  529. set nStartLimit 100000
  530. set nBackup 1
  531. }
  532. 3 { # Simulate transient failures with extra brute force.
  533. set nRepeat 100000
  534. set zName "ridiculous"
  535. set nStartLimit 1
  536. set nBackup 10
  537. }
  538. }
  539. # The set of acceptable results from running [catchsql $sql].
  540. #
  541. set answers [list {1 {out of memory}} $catchres]
  542. set str [join $answers " OR "]
  543. set nFail 1
  544. for {set iLimit $nStartLimit} {$nFail} {incr iLimit} {
  545. for {set iFail 1} {$nFail && $iFail<=$iLimit} {incr iFail} {
  546. for {set iTest 0} {$iTest<$nBackup && ($iFail-$iTest)>0} {incr iTest} {
  547. if {$isRestart} { sqlite3 db test.db }
  548. sqlite3_memdebug_fail [expr $iFail-$iTest] -repeat $nRepeat
  549. set res [uplevel [list catchsql $sql]]
  550. if {[lsearch -exact $answers $res]>=0} { set res $str }
  551. set testname "$name.$zName.$iFail"
  552. do_test "$name.$zName.$iLimit.$iFail" [list set {} $res] $str
  553. set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
  554. }
  555. }
  556. }
  557. }
  558. #-------------------------------------------------------------------------
  559. # Test a single write to the database. In this case a "write" is a
  560. # DELETE, UPDATE or INSERT statement.
  561. #
  562. # If OOM testing is performed, there are several acceptable outcomes:
  563. #
  564. # 1) The write succeeds. No error is returned.
  565. #
  566. # 2) An "out of memory" exception is thrown and:
  567. #
  568. # a) The statement has no effect, OR
  569. # b) The current transaction is rolled back, OR
  570. # c) The statement succeeds. This can only happen if the connection
  571. # is in auto-commit mode (after the statement is executed, so this
  572. # includes COMMIT statements).
  573. #
  574. # If the write operation eventually succeeds, zero is returned. If a
  575. # transaction is rolled back, non-zero is returned.
  576. #
  577. # Parameter $name is the name to use for the test case (or test cases).
  578. # The second parameter, $tbl, should be the name of the database table
  579. # being modified. Parameter $sql contains the SQL statement to test.
  580. #
  581. proc do_write_test {name tbl sql} {
  582. if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
  583. # Figure out an statement to get a checksum for table $tbl.
  584. db eval "SELECT * FROM $tbl" V break
  585. set cksumsql "SELECT md5sum([join [concat rowid $V(*)] ,]) FROM $tbl"
  586. # Calculate the initial table checksum.
  587. set cksum1 [db one $cksumsql]
  588. if {$::DO_MALLOC_TEST } {
  589. set answers [list {1 {out of memory}} {0 {}}]
  590. if {$::DO_MALLOC_TEST==1} {
  591. set modes {100000 persistent}
  592. } else {
  593. set modes {1 transient}
  594. }
  595. } else {
  596. set answers [list {0 {}}]
  597. set modes [list 0 nofail]
  598. }
  599. set str [join $answers " OR "]
  600. foreach {nRepeat zName} $modes {
  601. for {set iFail 1} 1 {incr iFail} {
  602. if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat}
  603. set res [uplevel [list catchsql $sql]]
  604. set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
  605. if {$nFail==0} {
  606. do_test $name.$zName.$iFail [list set {} $res] {0 {}}
  607. return
  608. } else {
  609. if {[lsearch $answers $res]>=0} {
  610. set res $str
  611. }
  612. do_test $name.$zName.$iFail [list set {} $res] $str
  613. set cksum2 [db one $cksumsql]
  614. if {$cksum1 != $cksum2} return
  615. }
  616. }
  617. }
  618. }