1# 2007 April 25 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# This file contains additional out-of-memory checks (see malloc.tcl) 12# added to expose a bug in out-of-memory handling for sqlite3_value_text() 13# 14# $Id: malloc8.test,v 1.3 2007/05/07 19:31:17 drh Exp $ 15 16set testdir [file dirname $argv0] 17source $testdir/tester.tcl 18 19# Only run these tests if memory debugging is turned on. 20# 21if {[info command sqlite_malloc_stat]==""} { 22 puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..." 23 finish_test 24 return 25} 26 27# Usage: do_malloc_test <test number> <options...> 28# 29# The first argument, <test number>, is an integer used to name the 30# tests executed by this proc. Options are as follows: 31# 32# -tclprep TCL script to run to prepare test. 33# -sqlprep SQL script to run to prepare test. 34# -tclbody TCL script to run with malloc failure simulation. 35# -sqlbody TCL script to run with malloc failure simulation. 36# -cleanup TCL script to run after the test. 37# 38# This command runs a series of tests to verify SQLite's ability 39# to handle an out-of-memory condition gracefully. It is assumed 40# that if this condition occurs a malloc() call will return a 41# NULL pointer. Linux, for example, doesn't do that by default. See 42# the "BUGS" section of malloc(3). 43# 44# Each iteration of a loop, the TCL commands in any argument passed 45# to the -tclbody switch, followed by the SQL commands in any argument 46# passed to the -sqlbody switch are executed. Each iteration the 47# Nth call to sqliteMalloc() is made to fail, where N is increased 48# each time the loop runs starting from 1. When all commands execute 49# successfully, the loop ends. 50# 51proc do_malloc_test {tn args} { 52 array unset ::mallocopts 53 array set ::mallocopts $args 54 55 set ::go 1 56 for {set ::n 1} {$::go && $::n < 50000} {incr ::n} { 57 do_test malloc8-$tn.$::n { 58 59 sqlite_malloc_fail 0 60 catch {db close} 61 sqlite3 db test.db 62 set ::DB [sqlite3_connection_pointer db] 63 64 # Execute any -tclprep and -sqlprep scripts. 65 # 66 if {[info exists ::mallocopts(-tclprep)]} { 67 eval $::mallocopts(-tclprep) 68 } 69 if {[info exists ::mallocopts(-sqlprep)]} { 70 execsql $::mallocopts(-sqlprep) 71 } 72 73 # Now set the ${::n}th malloc() to fail and execute the -tclbody and 74 # -sqlbody scripts. 75 # 76 sqlite_malloc_fail $::n 77 set ::mallocbody {} 78 if {[info exists ::mallocopts(-tclbody)]} { 79 append ::mallocbody "$::mallocopts(-tclbody)\n" 80 } 81 if {[info exists ::mallocopts(-sqlbody)]} { 82 append ::mallocbody "db eval {$::mallocopts(-sqlbody)}" 83 } 84 set v [catch $::mallocbody msg] 85 86 # If the test fails (if $v!=0) and the database connection actually 87 # exists, make sure the failure code is SQLITE_NOMEM. 88 if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)] 89 && [db errorcode]!=7} { 90 set v 999 91 } 92 93 set leftover [lindex [sqlite_malloc_stat] 2] 94 if {$leftover>0} { 95 if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"} 96 set ::go 0 97 if {$v} { 98 puts "\nError message returned: $msg" 99 } else { 100 set v {1 1} 101 } 102 } else { 103 set v2 [expr {$msg=="" || $msg=="out of memory"}] 104 if {!$v2} {puts "\nError message returned: $msg"} 105 lappend v $v2 106 } 107 } {1 1} 108 109 if {[info exists ::mallocopts(-cleanup)]} { 110 catch [list uplevel #0 $::mallocopts(-cleanup)] msg 111 } 112 } 113 unset ::mallocopts 114} 115 116# The setup is a database with UTF-16 encoding that contains a single 117# large string. We will be running lots of queries against this 118# database. Because we will be extracting the string as UTF-8, there 119# is a type conversion that occurs and thus an opportunity for malloc() 120# to fail and for sqlite3_value_text() to return 0 even though 121# sqlite3_value_type() returns SQLITE_TEXT. 122# 123db close 124file delete -force test.db test.db-journal 125sqlite3 db test.db 126db eval { 127 PRAGMA encoding='UTF-16'; 128 CREATE TABLE t1(a); 129 INSERT INTO t1 130 VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ'); 131} 132 133 134do_malloc_test 1 -sqlbody { 135 SELECT lower(a), upper(a), quote(a), trim(a), trim('x',a) FROM t1; 136} 137do_malloc_test 2 -sqlbody { 138 SELECT replace(a,'x','y'), replace('x',a,'y'), replace('x','y',a) 139 FROM t1; 140} 141do_malloc_test 3 -sqlbody { 142 SELECT length(a), substr(a, 4, 4) FROM t1; 143} 144do_malloc_test 4 -sqlbody { 145 SELECT julianday(a,a) FROM t1; 146} 147do_malloc_test 5 -sqlbody { 148 SELECT 1 FROM t1 WHERE a LIKE 'hello' ESCAPE NULL; 149} 150do_malloc_test 6 -sqlbody { 151 SELECT hex(randomblob(100)); 152} 153 154# Ensure that no file descriptors were leaked. 155do_test malloc-99.X { 156 catch {db close} 157 set sqlite_open_file_count 158} {0} 159 160sqlite_malloc_fail 0 161finish_test 162