sqlite/test/malloc2.test
drh eee4c8ca11 Add the memory fault simulator to mem5.c. Enable soft heap limit on mem5.c.
Limit the size of hash tables and the vdbefifo when using mem5.c. (CVS 4795)

FossilOrigin-Name: 63da5d97542e4f54c33329833477c8d96ce05dd0
2008-02-18 22:24:57 +00:00

345 lines
8.7 KiB
Plaintext

# 2005 March 18
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file attempts to check that the library can recover from a malloc()
# failure when sqlite3_global_recover() is invoked.
#
# (Later:) The sqlite3_global_recover() interface is now a no-op.
# Recovery from malloc() failures is automatic. But we keep these
# tests around because you can never have too many test cases.
#
# $Id: malloc2.test,v 1.12 2008/02/18 22:24:58 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
# Only run these tests if memory debugging is turned on.
#
if {!$MEMDEBUG} {
puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
finish_test
return
}
sqlite3_extended_result_codes db 1
proc do_malloc2_test {tn args} {
array set ::mallocopts $args
set sum [allcksum db]
save_prng_state
for {set ::n 784} {true} {incr ::n} {
# Run the SQL. Malloc number $::n is set to fail. A malloc() failure
# may or may not be reported.
restore_prng_state
sqlite3_memdebug_fail $::n -repeat 1
do_test malloc2-$tn.$::n.2 {
set res [catchsql [string trim $::mallocopts(-sql)]]
set rc [expr {
0==[string compare $res {1 {out of memory}}] ||
[db errorcode] == 3082 ||
0==[lindex $res 0]
}]
if {$rc!=1} {
puts "Error: $res"
}
set rc
} {1}
# If $::n is greater than the number of malloc() calls required to
# execute the SQL, then this test is finished. Break out of the loop.
set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
incr nFail [expr {-1*$nBenign}]
if {$nFail==0} break
# Nothing should work now, because the allocator should refuse to
# allocate any memory.
#
# Update: SQLite now automatically recovers from a malloc() failure.
# So the statement in the test below would work.
if 0 {
do_test malloc2-$tn.$::n.3 {
catchsql {SELECT 'nothing should work'}
} {1 {out of memory}}
}
# Recover from the malloc failure.
#
# Update: The new malloc() failure handling means that a transaction may
# still be active even if a malloc() has failed. But when these tests were
# written this was not the case. So do a manual ROLLBACK here so that the
# tests pass.
do_test malloc2-$tn.$::n.4 {
sqlite3_global_recover
catch {
execsql {
ROLLBACK;
}
}
expr 0
} {0}
# Checksum the database.
do_test malloc2-$tn.$::n.5 {
allcksum db
} $sum
integrity_check malloc2-$tn.$::n.6
if {$::nErr>1} return
}
unset ::mallocopts
}
do_test malloc2.1.setup {
execsql {
CREATE TABLE abc(a, b, c);
INSERT INTO abc VALUES(10, 20, 30);
INSERT INTO abc VALUES(40, 50, 60);
CREATE INDEX abc_i ON abc(a, b, c);
}
} {}
do_malloc2_test 1.1 -sql {
SELECT * FROM abc;
}
do_malloc2_test 1.2 -sql {
UPDATE abc SET c = c+10;
}
do_malloc2_test 1.3 -sql {
INSERT INTO abc VALUES(70, 80, 90);
}
do_malloc2_test 1.4 -sql {
DELETE FROM abc;
}
do_test malloc2.1.5 {
execsql {
SELECT * FROM abc;
}
} {}
do_test malloc2.2.setup {
execsql {
CREATE TABLE def(a, b, c);
CREATE INDEX def_i1 ON def(a);
CREATE INDEX def_i2 ON def(c);
BEGIN;
}
for {set i 0} {$i<20} {incr i} {
execsql {
INSERT INTO def VALUES(randstr(300,300),randstr(300,300),randstr(300,300));
}
}
execsql {
COMMIT;
}
} {}
do_malloc2_test 2 -sql {
BEGIN;
UPDATE def SET a = randstr(100,100) WHERE (oid%9)==0;
INSERT INTO def SELECT * FROM def WHERE (oid%13)==0;
CREATE INDEX def_i3 ON def(b);
UPDATE def SET a = randstr(100,100) WHERE (oid%9)==1;
INSERT INTO def SELECT * FROM def WHERE (oid%13)==1;
CREATE TABLE def2 AS SELECT * FROM def;
DROP TABLE def;
CREATE TABLE def AS SELECT * FROM def2;
DROP TABLE def2;
DELETE FROM def WHERE (oid%9)==2;
INSERT INTO def SELECT * FROM def WHERE (oid%13)==2;
COMMIT;
}
ifcapable tempdb {
do_test malloc2.3.setup {
execsql {
CREATE TEMP TABLE ghi(a, b, c);
BEGIN;
}
for {set i 0} {$i<20} {incr i} {
execsql {
INSERT INTO ghi VALUES(
randstr(300,300), randstr(300,300), randstr(300,300)
);
}
}
execsql {
COMMIT;
}
} {}
do_malloc2_test 3 -sql {
BEGIN;
CREATE INDEX ghi_i1 ON ghi(a);
UPDATE def SET a = randstr(100,100) WHERE (oid%2)==0;
UPDATE ghi SET a = randstr(100,100) WHERE (oid%2)==0;
COMMIT;
}
}
############################################################################
# The test cases below are to increase the code coverage in btree.c and
# pager.c of this test file. The idea is that each malloc() that occurs in
# these two source files should be made to fail at least once.
#
catchsql {
DROP TABLE ghi;
}
do_malloc2_test 4.1 -sql {
SELECT * FROM def ORDER BY oid ASC;
SELECT * FROM def ORDER BY oid DESC;
}
do_malloc2_test 4.2 -sql {
PRAGMA cache_size = 10;
BEGIN;
-- This will put about 25 pages on the free list.
DELETE FROM def WHERE 1;
-- Allocate 32 new root pages. This will exercise the 'extract specific
-- page from the freelist' code when in auto-vacuum mode (see the
-- allocatePage() routine in btree.c).
CREATE TABLE t1(a UNIQUE, b UNIQUE, c UNIQUE);
CREATE TABLE t2(a UNIQUE, b UNIQUE, c UNIQUE);
CREATE TABLE t3(a UNIQUE, b UNIQUE, c UNIQUE);
CREATE TABLE t4(a UNIQUE, b UNIQUE, c UNIQUE);
CREATE TABLE t5(a UNIQUE, b UNIQUE, c UNIQUE);
CREATE TABLE t6(a UNIQUE, b UNIQUE, c UNIQUE);
CREATE TABLE t7(a UNIQUE, b UNIQUE, c UNIQUE);
CREATE TABLE t8(a UNIQUE, b UNIQUE, c UNIQUE);
ROLLBACK;
}
########################################################################
# Test that the global linked list of database handles works. An assert()
# will fail if there is some problem.
do_test malloc2-5 {
sqlite3 db1 test.db
sqlite3 db2 test.db
sqlite3 db3 test.db
sqlite3 db4 test.db
sqlite3 db5 test.db
sqlite3_extended_result_codes db1 1
sqlite3_extended_result_codes db2 1
sqlite3_extended_result_codes db3 1
sqlite3_extended_result_codes db4 1
sqlite3_extended_result_codes db5 1
# Close the head of the list:
db5 close
# Close the end of the list:
db1 close
# Close a handle from the middle of the list:
db3 close
# Close the other two. Then open and close one more database, to make
# sure the head of the list was set back to NULL.
db2 close
db4 close
sqlite db1 test.db
db1 close
} {}
########################################################################
# Check that if a statement is active sqlite3_global_recover doesn't reset
# the sqlite3_malloc_failed variable.
#
# Update: There is now no sqlite3_malloc_failed variable, so these tests
# are not run.
#
# do_test malloc2-6.1 {
# set ::STMT [sqlite3_prepare $::DB {SELECT * FROM def} -1 DUMMY]
# sqlite3_step $::STMT
# } {SQLITE_ROW}
# do_test malloc2-6.2 {
# sqlite3 db1 test.db
# sqlite_malloc_fail 100
# catchsql {
# SELECT * FROM def;
# } db1
# } {1 {out of memory}}
# do_test malloc2-6.3 {
# sqlite3_global_recover
# } {SQLITE_BUSY}
# do_test malloc2-6.4 {
# catchsql {
# SELECT 'hello';
# }
# } {1 {out of memory}}
# do_test malloc2-6.5 {
# sqlite3_reset $::STMT
# } {SQLITE_OK}
# do_test malloc2-6.6 {
# sqlite3_global_recover
# } {SQLITE_OK}
# do_test malloc2-6.7 {
# catchsql {
# SELECT 'hello';
# }
# } {0 hello}
# do_test malloc2-6.8 {
# sqlite3_step $::STMT
# } {SQLITE_ERROR}
# do_test malloc2-6.9 {
# sqlite3_finalize $::STMT
# } {SQLITE_SCHEMA}
# do_test malloc2-6.10 {
# db1 close
# } {}
########################################################################
# Check that if an in-memory database is being used it is not possible
# to recover from a malloc() failure.
#
# Update: An in-memory database can now survive a malloc() failure, so these
# tests are not run.
#
# ifcapable memorydb {
# do_test malloc2-7.1 {
# sqlite3 db1 :memory:
# list
# } {}
# do_test malloc2-7.2 {
# sqlite_malloc_fail 100
# catchsql {
# SELECT * FROM def;
# }
# } {1 {out of memory}}
# do_test malloc2-7.3 {
# sqlite3_global_recover
# } {SQLITE_ERROR}
# do_test malloc2-7.4 {
# catchsql {
# SELECT 'hello';
# }
# } {1 {out of memory}}
# do_test malloc2-7.5 {
# db1 close
# } {}
# do_test malloc2-7.6 {
# sqlite3_global_recover
# } {SQLITE_OK}
# do_test malloc2-7.7 {
# catchsql {
# SELECT 'hello';
# }
# } {0 hello}
# }
finish_test