1# 2006 June 10 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: vtab_err.test,v 1.3 2006/08/15 14:21:16 drh Exp $ 13 14set testdir [file dirname $argv0] 15source $testdir/tester.tcl 16 17ifcapable !vtab { 18 finish_test 19 return 20} 21 22# Usage: do_malloc_test <test number> <options...> 23# 24# The first argument, <test number>, is an integer used to name the 25# tests executed by this proc. Options are as follows: 26# 27# -tclprep TCL script to run to prepare test. 28# -sqlprep SQL script to run to prepare test. 29# -tclbody TCL script to run with malloc failure simulation. 30# -sqlbody TCL script to run with malloc failure simulation. 31# -cleanup TCL script to run after the test. 32# 33# This command runs a series of tests to verify SQLite's ability 34# to handle an out-of-memory condition gracefully. It is assumed 35# that if this condition occurs a malloc() call will return a 36# NULL pointer. Linux, for example, doesn't do that by default. See 37# the "BUGS" section of malloc(3). 38# 39# Each iteration of a loop, the TCL commands in any argument passed 40# to the -tclbody switch, followed by the SQL commands in any argument 41# passed to the -sqlbody switch are executed. Each iteration the 42# Nth call to sqliteMalloc() is made to fail, where N is increased 43# each time the loop runs starting from 1. When all commands execute 44# successfully, the loop ends. 45# 46proc do_malloc_test {tn args} { 47 array unset ::mallocopts 48 array set ::mallocopts $args 49 50 set ::go 1 51 for {set ::n 1} {$::go && $::n < 50000} {incr ::n} { 52 do_test $tn.$::n { 53 54 # Remove all traces of database files test.db and test2.db from the files 55 # system. Then open (empty database) "test.db" with the handle [db]. 56 # 57 sqlite_malloc_fail 0 58 catch {db close} 59 catch {file delete -force test.db} 60 catch {file delete -force test.db-journal} 61 catch {file delete -force test2.db} 62 catch {file delete -force test2.db-journal} 63 catch {sqlite3 db test.db} 64 set ::DB [sqlite3_connection_pointer db] 65 66 # Execute any -tclprep and -sqlprep scripts. 67 # 68 if {[info exists ::mallocopts(-tclprep)]} { 69 eval $::mallocopts(-tclprep) 70 } 71 if {[info exists ::mallocopts(-sqlprep)]} { 72 execsql $::mallocopts(-sqlprep) 73 } 74 75 # Now set the ${::n}th malloc() to fail and execute the -tclbody and 76 # -sqlbody scripts. 77 # 78 sqlite_malloc_fail $::n 79 set ::mallocbody {} 80 if {[info exists ::mallocopts(-tclbody)]} { 81 append ::mallocbody "$::mallocopts(-tclbody)\n" 82 } 83 if {[info exists ::mallocopts(-sqlbody)]} { 84 append ::mallocbody "db eval {$::mallocopts(-sqlbody)}" 85 } 86 set v [catch $::mallocbody msg] 87 88 # If the test fails (if $v!=0) and the database connection actually 89 # exists, make sure the failure code is SQLITE_NOMEM. 90 if {$v&&[info command db]=="db"&&[info exists ::mallocopts(-sqlbody)]} { 91 if {[db errorcode]!=7 && $msg!="vtable constructor failed: e"} { 92 set v 999 93 } 94 } 95 96 set leftover [lindex [sqlite_malloc_stat] 2] 97 if {$leftover>0} { 98 if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"} 99 set ::go 0 100 if {$v} { 101 puts "\nError message returned: $msg" 102 } else { 103 set v {1 1} 104 } 105 } else { 106 set v2 [expr { 107 $msg == "" || $msg == "out of memory" || 108 $msg == "vtable constructor failed: e" 109 }] 110 if {!$v2} {puts "\nError message returned: $msg"} 111 lappend v $v2 112 } 113 } {1 1} 114 115 if {[info exists ::mallocopts(-cleanup)]} { 116 catch [list uplevel #0 $::mallocopts(-cleanup)] msg 117 } 118 } 119 unset ::mallocopts 120} 121 122unset -nocomplain echo_module_begin_fail 123do_ioerr_test vtab_err-1 -tclprep { 124 register_echo_module [sqlite3_connection_pointer db] 125} -sqlbody { 126 BEGIN; 127 CREATE TABLE r(a PRIMARY KEY, b, c); 128 CREATE VIRTUAL TABLE e USING echo(r); 129 INSERT INTO e VALUES(1, 2, 3); 130 INSERT INTO e VALUES('a', 'b', 'c'); 131 UPDATE e SET c = 10; 132 DELETE FROM e WHERE a = 'a'; 133 COMMIT; 134 BEGIN; 135 CREATE TABLE r2(a, b, c); 136 INSERT INTO r2 SELECT * FROM e; 137 INSERT INTO e SELECT a||'x', b, c FROM r2; 138 COMMIT; 139} 140 141 142do_malloc_test vtab_err-2 -tclprep { 143 register_echo_module [sqlite3_connection_pointer db] 144} -sqlbody { 145 BEGIN; 146 CREATE TABLE r(a PRIMARY KEY, b, c); 147 CREATE VIRTUAL TABLE e USING echo(r); 148 INSERT INTO e VALUES(1, 2, 3); 149 INSERT INTO e VALUES('a', 'b', 'c'); 150 UPDATE e SET c = 10; 151 DELETE FROM e WHERE a = 'a'; 152 COMMIT; 153 BEGIN; 154 CREATE TABLE r2(a, b, c); 155 INSERT INTO r2 SELECT * FROM e; 156 INSERT INTO e SELECT a||'x', b, c FROM r2; 157 COMMIT; 158} 159 160sqlite_malloc_fail 0 161finish_test 162