1# 2001 September 15 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 attempts to check the library in an out-of-memory situation. 12# When compiled with -DSQLITE_DEBUG=1, the SQLite library accepts a special 13# command (sqlite_malloc_fail N) which causes the N-th malloc to fail. This 14# special feature is used to see what happens in the library if a malloc 15# were to really fail due to an out-of-memory situation. 16# 17# $Id: malloc.test,v 1.22 2005/01/22 03:39:39 danielk1977 Exp $ 18 19set testdir [file dirname $argv0] 20source $testdir/tester.tcl 21 22# Only run these tests if memory debugging is turned on. 23# 24if {[info command sqlite_malloc_stat]==""} { 25 puts "Skipping malloc tests: not compiled with -DSQLITE_DEBUG..." 26 finish_test 27 return 28} 29 30# Usage: do_malloc_test <test number> <options...> 31# 32# The first argument, <test number>, is an integer used to name the 33# tests executed by this proc. Options are as follows: 34# 35# -tclprep TCL script to run to prepare test. 36# -sqlprep SQL script to run to prepare test. 37# -tclbody TCL script to run with malloc failure simulation. 38# -sqlbody TCL script to run with malloc failure simulation. 39# -cleanup TCL script to run after the test. 40# 41# This command runs a series of tests to verify SQLite's ability 42# to handle an out-of-memory condition gracefully. It is assumed 43# that if this condition occurs a malloc() call will return a 44# NULL pointer. Linux, for example, doesn't do that by default. See 45# the "BUGS" section of malloc(3). 46# 47# Each iteration of a loop, the TCL commands in any argument passed 48# to the -tclbody switch, followed by the SQL commands in any argument 49# passed to the -sqlbody switch are executed. Each iteration the 50# Nth call to sqliteMalloc() is made to fail, where N is increased 51# each time the loop runs starting from 1. When all commands execute 52# successfully, the loop ends. 53# 54proc do_malloc_test {tn args} { 55 array set ::mallocopts $args 56 57 set ::go 1 58 for {set ::n 1} {$::go} {incr ::n} { 59 60 do_test malloc-$tn.$::n { 61 62 sqlite_malloc_fail 0 63 catch {db close} 64 catch {file delete -force test.db} 65 catch {file delete -force test.db-journal} 66 catch {file delete -force test2.db} 67 catch {file delete -force test2.db-journal} 68 set ::DB [sqlite3 db test.db] 69 70 if {[info exists ::mallocopts(-tclprep)]} { 71 eval $::mallocopts(-tclprep) 72 } 73 if {[info exists ::mallocopts(-sqlprep)]} { 74 execsql $::mallocopts(-sqlprep) 75 } 76 77 sqlite_malloc_fail $::n 78 set ::mallocbody {} 79 if {[info exists ::mallocopts(-tclbody)]} { 80 append ::mallocbody "$::mallocopts(-tclbody)\n" 81 } 82 if {[info exists ::mallocopts(-sqlbody)]} { 83 append ::mallocbody "db eval {$::mallocopts(-sqlbody)}" 84 } 85 86 set v [catch $::mallocbody msg] 87 88 set leftover [lindex [sqlite_malloc_stat] 2] 89 if {$leftover>0} { 90 if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"} 91 set ::go 0 92 set v {1 1} 93 } else { 94 set v2 [expr {$msg=="" || $msg=="out of memory"}] 95 if {!$v2} {puts "\nError message returned: $msg"} 96 lappend v $v2 97 } 98 } {1 1} 99 100 if {[info exists ::mallocopts(-cleanup)]} { 101 catch $::mallocopts(-cleanup) 102 } 103 } 104 unset ::mallocopts 105} 106 107do_malloc_test 1 -tclprep { 108 db close 109} -tclbody { 110 if {[catch {sqlite3 db test.db}]} { 111 error "out of memory" 112 } 113} -sqlbody { 114 CREATE TABLE t1( 115 a int, b float, c double, d text, e varchar(20), 116 primary key(a,b,c) 117 ); 118 CREATE INDEX i1 ON t1(a,b); 119 INSERT INTO t1 VALUES(1,2.3,4.5,'hi','there'); 120 INSERT INTO t1 VALUES(6,7.0,0.8,'hello','out yonder'); 121 SELECT * FROM t1; 122 SELECT avg(b) FROM t1 GROUP BY a HAVING b>20.0; 123 DELETE FROM t1 WHERE a IN (SELECT min(a) FROM t1); 124 SELECT count(*) FROM t1; 125} 126 127# Ensure that no file descriptors were leaked. 128do_test malloc-1.X { 129 catch {db close} 130 set sqlite_open_file_count 131} {0} 132 133do_malloc_test 2 -sqlbody { 134 CREATE TABLE t1(a int, b int, c int); 135 CREATE INDEX i1 ON t1(a,b); 136 INSERT INTO t1 VALUES(1,1,'99 abcdefghijklmnopqrstuvwxyz'); 137 INSERT INTO t1 VALUES(2,4,'98 abcdefghijklmnopqrstuvwxyz'); 138 INSERT INTO t1 VALUES(3,9,'97 abcdefghijklmnopqrstuvwxyz'); 139 INSERT INTO t1 VALUES(4,16,'96 abcdefghijklmnopqrstuvwxyz'); 140 INSERT INTO t1 VALUES(5,25,'95 abcdefghijklmnopqrstuvwxyz'); 141 INSERT INTO t1 VALUES(6,36,'94 abcdefghijklmnopqrstuvwxyz'); 142 SELECT 'stuff', count(*) as 'other stuff', max(a+10) FROM t1; 143 UPDATE t1 SET b=b||b||b||b; 144 UPDATE t1 SET b=a WHERE a in (10,12,22); 145 INSERT INTO t1(c,b,a) VALUES(20,10,5); 146 INSERT INTO t1 SELECT * FROM t1 147 WHERE a IN (SELECT a FROM t1 WHERE a<10); 148 DELETE FROM t1 WHERE a>=10; 149 DROP INDEX i1; 150 DELETE FROM t1; 151} 152 153# Ensure that no file descriptors were leaked. 154do_test malloc-2.X { 155 catch {db close} 156 set sqlite_open_file_count 157} {0} 158 159do_malloc_test 3 -sqlbody { 160 BEGIN TRANSACTION; 161 CREATE TABLE t1(a int, b int, c int); 162 CREATE INDEX i1 ON t1(a,b); 163 INSERT INTO t1 VALUES(1,1,99); 164 INSERT INTO t1 VALUES(2,4,98); 165 INSERT INTO t1 VALUES(3,9,97); 166 INSERT INTO t1 VALUES(4,16,96); 167 INSERT INTO t1 VALUES(5,25,95); 168 INSERT INTO t1 VALUES(6,36,94); 169 INSERT INTO t1(c,b,a) VALUES(20,10,5); 170 DELETE FROM t1 WHERE a>=10; 171 DROP INDEX i1; 172 DELETE FROM t1; 173 ROLLBACK; 174} 175 176 177# Ensure that no file descriptors were leaked. 178do_test malloc-3.X { 179 catch {db close} 180 set sqlite_open_file_count 181} {0} 182 183do_malloc_test 4 -sqlbody { 184 BEGIN TRANSACTION; 185 CREATE TABLE t1(a int, b int, c int); 186 CREATE INDEX i1 ON t1(a,b); 187 INSERT INTO t1 VALUES(1,1,99); 188 INSERT INTO t1 VALUES(2,4,98); 189 INSERT INTO t1 VALUES(3,9,97); 190 INSERT INTO t1 VALUES(4,16,96); 191 INSERT INTO t1 VALUES(5,25,95); 192 INSERT INTO t1 VALUES(6,36,94); 193 UPDATE t1 SET b=a WHERE a in (10,12,22); 194 INSERT INTO t1 SELECT * FROM t1 195 WHERE a IN (SELECT a FROM t1 WHERE a<10); 196 DROP INDEX i1; 197 DELETE FROM t1; 198 COMMIT; 199} 200 201# Ensure that no file descriptors were leaked. 202do_test malloc-4.X { 203 catch {db close} 204 set sqlite_open_file_count 205} {0} 206 207do_malloc_test 5 -sqlbody { 208 BEGIN TRANSACTION; 209 CREATE TABLE t1(a,b); 210 CREATE TABLE t2(x,y); 211 CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN 212 INSERT INTO t2(x,y) VALUES(new.rowid,1); 213 END; 214 INSERT INTO t1(a,b) VALUES(2,3); 215 COMMIT; 216} 217 218# Ensure that no file descriptors were leaked. 219do_test malloc-5.X { 220 catch {db close} 221 set sqlite_open_file_count 222} {0} 223 224do_malloc_test 6 -sqlprep { 225 BEGIN TRANSACTION; 226 CREATE TABLE t1(a); 227 INSERT INTO t1 VALUES(1); 228 INSERT INTO t1 SELECT a*2 FROM t1; 229 INSERT INTO t1 SELECT a*2 FROM t1; 230 INSERT INTO t1 SELECT a*2 FROM t1; 231 INSERT INTO t1 SELECT a*2 FROM t1; 232 INSERT INTO t1 SELECT a*2 FROM t1; 233 INSERT INTO t1 SELECT a*2 FROM t1; 234 INSERT INTO t1 SELECT a*2 FROM t1; 235 INSERT INTO t1 SELECT a*2 FROM t1; 236 INSERT INTO t1 SELECT a*2 FROM t1; 237 INSERT INTO t1 SELECT a*2 FROM t1; 238 DELETE FROM t1 where rowid%5 = 0; 239 COMMIT; 240} -sqlbody { 241 VACUUM; 242} 243 244do_malloc_test 7 -sqlprep { 245 CREATE TABLE t1(a, b); 246 INSERT INTO t1 VALUES(1, 2); 247 INSERT INTO t1 VALUES(3, 4); 248 INSERT INTO t1 VALUES(5, 6); 249 INSERT INTO t1 VALUES(7, randstr(1200,1200)); 250} -sqlbody { 251 SELECT min(a) FROM t1 WHERE a<6 GROUP BY b; 252 SELECT a FROM t1 WHERE a<6 ORDER BY a; 253 SELECT b FROM t1 WHERE a>6; 254} 255 256# This block is designed to test that some malloc failures that may 257# occur in vdbeapi.c. Specifically, if a malloc failure that occurs 258# when converting UTF-16 text to integers and real numbers is handled 259# correctly. 260# 261# This is done by retrieving a string from the database engine and 262# manipulating it using the sqlite3_column_*** APIs. This doesn't 263# actually return an error to the user when a malloc() fails.. That 264# could be viewed as a bug. 265# 266# These tests only run if UTF-16 support is compiled in. 267# 268if {$::sqlite_options(utf16)} { 269 do_malloc_test 8 -tclprep { 270 set sql "SELECT '[string repeat abc 20]', '[string repeat def 20]', ?" 271 set ::STMT [sqlite3_prepare $::DB $sql -1 X] 272 sqlite3_step $::STMT 273 if { $::tcl_platform(byteOrder)=="littleEndian" } { 274 set ::bomstr "\xFF\xFE" 275 } else { 276 set ::bomstr "\xFE\xFF" 277 } 278 append ::bomstr [encoding convertto unicode "123456789_123456789_12345678"] 279 } -tclbody { 280 sqlite3_column_text16 $::STMT 0 281 sqlite3_column_int $::STMT 0 282 sqlite3_column_text16 $::STMT 1 283 sqlite3_column_double $::STMT 1 284 sqlite3_reset $::STMT 285 sqlite3_bind_text16 $::STMT 1 $::bomstr 60 286 catch {sqlite3_finalize $::STMT} 287 if {[lindex [sqlite_malloc_stat] 2]<=0} { 288 error "out of memory" 289 } 290 } -cleanup { 291 sqlite3_finalize $::STMT 292 } 293} 294 295# This block tests that malloc() failures that occur whilst commiting 296# a multi-file transaction are handled correctly. 297# 298do_malloc_test 9 -sqlprep { 299 ATTACH 'test2.db' as test2; 300 CREATE TABLE abc1(a, b, c); 301 CREATE TABLE test2.abc2(a, b, c); 302} -sqlbody { 303 BEGIN; 304 INSERT INTO abc1 VALUES(1, 2, 3); 305 INSERT INTO abc2 VALUES(1, 2, 3); 306 COMMIT; 307} 308 309# This block tests malloc() failures that occur while opening a 310# connection to a database. 311do_malloc_test 10 -sqlprep { 312 CREATE TABLE abc(a, b, c); 313} -tclbody { 314 set ::DB [sqlite3 db2 test.db] 315 db2 eval {SELECT * FROM sqlite_master} 316 db2 close 317} 318 319# This block tests malloc() failures that occur within calls to 320# sqlite3_create_function(). 321do_malloc_test 11 -tclbody { 322 set rc [sqlite3_create_function $::DB] 323 if {[string match $rc SQLITE_NOMEM]} { 324 error "out of memory" 325 } 326} 327 328do_malloc_test 12 -tclbody { 329 set sql16 [encoding convertto unicode "SELECT * FROM sqlite_master"] 330 append sql16 "\00\00" 331 set ::STMT [sqlite3_prepare16 $::DB $sql16 -1 DUMMY] 332 sqlite3_finalize $::STMT 333} 334 335# Test malloc errors when replaying two hot journals from a 2-file 336# transaction. This test only runs on UNIX. 337if {$tcl_platform(platform)=="unix"} { 338 do_malloc_test 13 -tclprep { 339 set rc [crashsql 1 test2.db { 340 ATTACH 'test2.db' as aux; 341 PRAGMA cache_size = 10; 342 BEGIN; 343 CREATE TABLE aux.t2(a, b, c); 344 CREATE TABLE t1(a, b, c); 345 COMMIT; 346 }] 347 if {$rc!="1 {child process exited abnormally}"} { 348 error "Wrong error message: $rc" 349 } 350 } -sqlbody { 351 ATTACH 'test2.db' as aux; 352 SELECT * FROM t1; 353 SELECT * FROM t2; 354 } 355} 356 357if {$tcl_platform(platform)!="windows"} { 358do_malloc_test 14 -tclprep { 359 catch {db close} 360 sqlite3 db2 test2.db 361 db2 eval { 362 PRAGMA synchronous = 0; 363 CREATE TABLE t1(a, b); 364 INSERT INTO t1 VALUES(1, 2); 365 BEGIN; 366 INSERT INTO t1 VALUES(3, 4); 367 } 368 copy_file test2.db test.db 369 copy_file test2.db-journal test.db-journal 370 db2 close 371} -tclbody { 372 sqlite3 db test.db 373 db eval { 374 SELECT * FROM t1; 375 } 376} 377} 378 379# Ensure that no file descriptors were leaked. 380do_test malloc-99.X { 381 catch {db close} 382 set sqlite_open_file_count 383} {0} 384 385sqlite_malloc_fail 0 386finish_test 387