blob: bce5b0141c57f6c7886e5e351f7297b49714f42e [file] [log] [blame]
danielk19776b456a22005-03-21 04:04:02 +00001# 2005 March 18
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 that the library can recover from a malloc()
12# failure when sqlite3_global_recover() is invoked.
13#
danielk1977261919c2005-12-06 12:52:59 +000014# $Id: malloc2.test,v 1.4 2005/12/06 12:53:01 danielk1977 Exp $
danielk19776b456a22005-03-21 04:04:02 +000015
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_DEBUG..."
23 finish_test
24 return
25}
26
27ifcapable !globalrecover {
28 finish_test
29 return
30}
31
32# Generate a checksum based on the contents of the database. If the
33# checksum of two databases is the same, and the integrity-check passes
34# for both, the two databases are identical.
35#
36proc cksum {db} {
37 set ret [list]
danielk197753c0f742005-03-29 03:10:59 +000038 ifcapable tempdb {
39 set sql {
40 SELECT name FROM sqlite_master WHERE type = 'table' UNION
41 SELECT name FROM sqlite_temp_master WHERE type = 'table' UNION
42 SELECT 'sqlite_master' UNION
43 SELECT 'sqlite_temp_master'
44 }
45 } else {
46 set sql {
47 SELECT name FROM sqlite_master WHERE type = 'table' UNION
48 SELECT 'sqlite_master'
49 }
danielk19776b456a22005-03-21 04:04:02 +000050 }
drhdefc9972005-06-06 14:45:42 +000051 set tbllist [$db eval $sql]
52 set txt {}
53 foreach tbl $tbllist {
54 append txt [$db eval "SELECT * FROM $tbl"]
danielk19776b456a22005-03-21 04:04:02 +000055 }
drhdefc9972005-06-06 14:45:42 +000056 # puts txt=$txt
57 return [md5 $txt]
danielk19776b456a22005-03-21 04:04:02 +000058}
59
60proc do_malloc2_test {tn args} {
61 array set ::mallocopts $args
62 set sum [cksum db]
63
64 for {set ::n 1} {true} {incr ::n} {
65
66 # Run the SQL. Malloc number $::n is set to fail. A malloc() failure
67 # may or may not be reported.
68 sqlite_malloc_fail $::n
69 do_test malloc2-$tn.$::n.2 {
drhdefc9972005-06-06 14:45:42 +000070 set res [catchsql [string trim $::mallocopts(-sql)]]
danielk19776b456a22005-03-21 04:04:02 +000071 set rc [expr {
72 0==[string compare $res {1 {out of memory}}] ||
73 0==[lindex $res 0]
74 }]
75 if {$rc!=1} {
76 puts "Error: $res"
77 }
78 set rc
79 } {1}
80
81 # If $::n is greater than the number of malloc() calls required to
82 # execute the SQL, then this test is finished. Break out of the loop.
83 if {[lindex [sqlite_malloc_stat] 2]>0} {
84 sqlite_malloc_fail -1
85 break
86 }
87
88 # Nothing should work now, because the allocator should refuse to
89 # allocate any memory.
danielk1977261919c2005-12-06 12:52:59 +000090 #
91 # Update: SQLite now automatically recovers from a malloc() failure.
92 # So the statement in the test below would work.
93if 0 {
danielk19776b456a22005-03-21 04:04:02 +000094 do_test malloc2-$tn.$::n.3 {
95 catchsql {SELECT 'nothing should work'}
96 } {1 {out of memory}}
danielk1977261919c2005-12-06 12:52:59 +000097}
danielk19776b456a22005-03-21 04:04:02 +000098
99 # Recover from the malloc failure.
danielk1977261919c2005-12-06 12:52:59 +0000100 #
101 # Update: The new malloc() failure handling means that a transaction may
102 # still be active even if a malloc() has failed. But when these tests were
103 # written this was not the case. So do a manual ROLLBACK here so that the
104 # tests pass.
danielk19776b456a22005-03-21 04:04:02 +0000105 do_test malloc2-$tn.$::n.4 {
danielk1977261919c2005-12-06 12:52:59 +0000106 sqlite3_global_recover
107 catch {
108 execsql {
109 ROLLBACK;
110 }
danielk19776b456a22005-03-21 04:04:02 +0000111 }
danielk1977261919c2005-12-06 12:52:59 +0000112 expr 0
113 } {0}
danielk19776b456a22005-03-21 04:04:02 +0000114
115 # Checksum the database.
116 do_test malloc2-$tn.$::n.5 {
117 cksum db
118 } $sum
119
120 integrity_check malloc2-$tn.$::n.6
121 if {$::nErr>1} return
122 }
123 unset ::mallocopts
124}
125
126do_test malloc2.1.setup {
127 execsql {
128 CREATE TABLE abc(a, b, c);
129 INSERT INTO abc VALUES(10, 20, 30);
130 INSERT INTO abc VALUES(40, 50, 60);
131 CREATE INDEX abc_i ON abc(a, b, c);
132 }
133} {}
134do_malloc2_test 1.1 -sql {
135 SELECT * FROM abc;
136}
137do_malloc2_test 1.2 -sql {
138 UPDATE abc SET c = c+10;
139}
140do_malloc2_test 1.3 -sql {
141 INSERT INTO abc VALUES(70, 80, 90);
142}
143do_malloc2_test 1.4 -sql {
144 DELETE FROM abc;
145}
146do_test malloc2.1.5 {
147 execsql {
148 SELECT * FROM abc;
149 }
150} {}
151
152do_test malloc2.2.setup {
153 execsql {
154 CREATE TABLE def(a, b, c);
155 CREATE INDEX def_i1 ON def(a);
156 CREATE INDEX def_i2 ON def(c);
157 BEGIN;
158 }
159 for {set i 0} {$i<20} {incr i} {
160 execsql {
161 INSERT INTO def VALUES(randstr(300,300),randstr(300,300),randstr(300,300));
162 }
163 }
164 execsql {
165 COMMIT;
166 }
167} {}
168do_malloc2_test 2 -sql {
169 BEGIN;
170 UPDATE def SET a = randstr(100,100) WHERE (oid%9)==0;
171 INSERT INTO def SELECT * FROM def WHERE (oid%13)==0;
172
173 CREATE INDEX def_i3 ON def(b);
174
175 UPDATE def SET a = randstr(100,100) WHERE (oid%9)==1;
176 INSERT INTO def SELECT * FROM def WHERE (oid%13)==1;
177
178 CREATE TABLE def2 AS SELECT * FROM def;
179 DROP TABLE def;
180 CREATE TABLE def AS SELECT * FROM def2;
181 DROP TABLE def2;
182
183 DELETE FROM def WHERE (oid%9)==2;
184 INSERT INTO def SELECT * FROM def WHERE (oid%13)==2;
185 COMMIT;
186}
187
danielk197753c0f742005-03-29 03:10:59 +0000188ifcapable tempdb {
189 do_test malloc2.3.setup {
danielk19776b456a22005-03-21 04:04:02 +0000190 execsql {
danielk197753c0f742005-03-29 03:10:59 +0000191 CREATE TEMP TABLE ghi(a, b, c);
192 BEGIN;
danielk19776b456a22005-03-21 04:04:02 +0000193 }
danielk197753c0f742005-03-29 03:10:59 +0000194 for {set i 0} {$i<20} {incr i} {
195 execsql {
196 INSERT INTO ghi VALUES(randstr(300,300),randstr(300,300),randstr(300,300));
197 }
198 }
199 execsql {
200 COMMIT;
201 }
202 } {}
203 do_malloc2_test 3 -sql {
204 BEGIN;
205 CREATE INDEX ghi_i1 ON ghi(a);
206 UPDATE def SET a = randstr(100,100) WHERE (oid%2)==0;
207 UPDATE ghi SET a = randstr(100,100) WHERE (oid%2)==0;
danielk19776b456a22005-03-21 04:04:02 +0000208 COMMIT;
209 }
danielk19776b456a22005-03-21 04:04:02 +0000210}
211
212############################################################################
213# The test cases below are to increase the code coverage in btree.c and
214# pager.c of this test file. The idea is that each malloc() that occurs in
215# these two source files should be made to fail at least once.
216#
217catchsql {
218 DROP TABLE ghi;
219}
220do_malloc2_test 4.1 -sql {
221 SELECT * FROM def ORDER BY oid ASC;
222 SELECT * FROM def ORDER BY oid DESC;
223}
224do_malloc2_test 4.2 -sql {
225 PRAGMA cache_size = 10;
226 BEGIN;
227
228 -- This will put about 25 pages on the free list.
229 DELETE FROM def WHERE 1;
230
231 -- Allocate 32 new root pages. This will exercise the 'extract specific
232 -- page from the freelist' code when in auto-vacuum mode (see the
233 -- allocatePage() routine in btree.c).
234 CREATE TABLE t1(a UNIQUE, b UNIQUE, c UNIQUE);
235 CREATE TABLE t2(a UNIQUE, b UNIQUE, c UNIQUE);
236 CREATE TABLE t3(a UNIQUE, b UNIQUE, c UNIQUE);
237 CREATE TABLE t4(a UNIQUE, b UNIQUE, c UNIQUE);
238 CREATE TABLE t5(a UNIQUE, b UNIQUE, c UNIQUE);
239 CREATE TABLE t6(a UNIQUE, b UNIQUE, c UNIQUE);
240 CREATE TABLE t7(a UNIQUE, b UNIQUE, c UNIQUE);
241 CREATE TABLE t8(a UNIQUE, b UNIQUE, c UNIQUE);
242
243 ROLLBACK;
244}
245
246########################################################################
247# Test that the global linked list of database handles works. An assert()
248# will fail if there is some problem.
249do_test malloc2-5 {
250 sqlite3 db1 test.db
251 sqlite3 db2 test.db
252 sqlite3 db3 test.db
253 sqlite3 db4 test.db
254 sqlite3 db5 test.db
255
256 # Close the head of the list:
257 db5 close
258
259 # Close the end of the list:
260 db1 close
261
262 # Close a handle from the middle of the list:
263 db3 close
264
265 # Close the other two. Then open and close one more database, to make
266 # sure the head of the list was set back to NULL.
267 db2 close
268 db4 close
269 sqlite db1 test.db
270 db1 close
271} {}
272
273########################################################################
274# Check that if a statement is active sqlite3_global_recover doesn't reset
275# the sqlite3_malloc_failed variable.
danielk1977261919c2005-12-06 12:52:59 +0000276#
277# Update: There is now no sqlite3_malloc_failed variable, so these tests
278# are not run.
279#
280# do_test malloc2-6.1 {
281# set ::STMT [sqlite3_prepare $::DB {SELECT * FROM def} -1 DUMMY]
282# sqlite3_step $::STMT
283# } {SQLITE_ROW}
284# do_test malloc2-6.2 {
285# sqlite3 db1 test.db
286# sqlite_malloc_fail 100
287# catchsql {
288# SELECT * FROM def;
289# } db1
290# } {1 {out of memory}}
291# do_test malloc2-6.3 {
292# sqlite3_global_recover
293# } {SQLITE_BUSY}
294# do_test malloc2-6.4 {
295# catchsql {
296# SELECT 'hello';
297# }
298# } {1 {out of memory}}
299# do_test malloc2-6.5 {
300# sqlite3_reset $::STMT
301# } {SQLITE_OK}
302# do_test malloc2-6.6 {
303# sqlite3_global_recover
304# } {SQLITE_OK}
305# do_test malloc2-6.7 {
306# catchsql {
307# SELECT 'hello';
308# }
309# } {0 hello}
310# do_test malloc2-6.8 {
311# sqlite3_step $::STMT
312# } {SQLITE_ERROR}
313# do_test malloc2-6.9 {
314# sqlite3_finalize $::STMT
315# } {SQLITE_SCHEMA}
316# do_test malloc2-6.10 {
317# db1 close
318# } {}
danielk19776b456a22005-03-21 04:04:02 +0000319
320########################################################################
321# Check that if an in-memory database is being used it is not possible
322# to recover from a malloc() failure.
danielk1977261919c2005-12-06 12:52:59 +0000323#
324# Update: An in-memory database can now survive a malloc() failure, so these
325# tests are not run.
326#
327# ifcapable memorydb {
328# do_test malloc2-7.1 {
329# sqlite3 db1 :memory:
330# list
331# } {}
332# do_test malloc2-7.2 {
333# sqlite_malloc_fail 100
334# catchsql {
335# SELECT * FROM def;
336# }
337# } {1 {out of memory}}
338# do_test malloc2-7.3 {
339# sqlite3_global_recover
340# } {SQLITE_ERROR}
341# do_test malloc2-7.4 {
342# catchsql {
343# SELECT 'hello';
344# }
345# } {1 {out of memory}}
346# do_test malloc2-7.5 {
347# db1 close
348# } {}
349# do_test malloc2-7.6 {
350# sqlite3_global_recover
351# } {SQLITE_OK}
352# do_test malloc2-7.7 {
353# catchsql {
354# SELECT 'hello';
355# }
356# } {0 hello}
357# }
danielk19776b456a22005-03-21 04:04:02 +0000358
359finish_test