blob: afbddf895c1864254c7746256f2afb9a37b9dd9d [file] [log] [blame]
danielk1977d1e3bee2004-06-22 13:12:51 +00001# 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 implements regression tests for SQLite library.
12#
drh5260f7e2004-06-26 19:35:29 +000013# The focus of this file is testing the ability of the database to
14# uses its rollback journal to recover intact (no database corruption)
15# from a power failure during the middle of a COMMIT. The special test
16# module "crashtest" compiled with the special "os_test.c" backend is used.
17# The os_test.c simulates the kind of file corruption that can occur
18# when writes are happening at the moment of power loss.
19#
20# The special crash-test module with its os_test.c backend only works
21# on Unix.
22#
danielk19774e17d142005-01-16 09:06:33 +000023# $Id: crash.test,v 1.16 2005/01/16 09:06:34 danielk1977 Exp $
danielk1977d1e3bee2004-06-22 13:12:51 +000024
25set testdir [file dirname $argv0]
26source $testdir/tester.tcl
27
danielk19778191bff2004-06-28 04:52:30 +000028# set repeats 100
29set repeats 10
danielk1977ef317ab2004-06-23 10:43:10 +000030
danielk1977ece80f12004-06-23 01:05:26 +000031# This proc execs a seperate process that crashes midway through executing
32# the SQL script $sql on database test.db.
33#
danielk1977ef317ab2004-06-23 10:43:10 +000034# The crash occurs during a sync() of file $crashfile. When the crash
35# occurs a random subset of all unsynced writes made by the process are
36# written into the files on disk. Argument $crashdelay indicates the
37# number of file syncs to wait before crashing.
38#
39# The return value is a list of two elements. The first element is a
40# boolean, indicating whether or not the process actually crashed or
41# reported some other error. The second element in the returned list is the
42# error message. This is "child process exited abnormally" if the crash
43# occured.
44proc crashsql {crashdelay crashfile sql} {
45 set cfile [file join [pwd] $crashfile]
danielk1977ece80f12004-06-23 01:05:26 +000046
danielk1977d1e3bee2004-06-22 13:12:51 +000047 set f [open crash.tcl w]
danielk1977ef317ab2004-06-23 10:43:10 +000048 puts $f "sqlite3_crashparams $crashdelay $cfile"
danielk1977ece80f12004-06-23 01:05:26 +000049 puts $f "sqlite3 db test.db"
danielk1977f187bd92004-06-25 06:23:22 +000050 puts $f "db eval {pragma cache_size = 10}"
danielk1977ece80f12004-06-23 01:05:26 +000051 puts $f "db eval {"
52 puts $f "$sql"
53 puts $f "}"
danielk1977d1e3bee2004-06-22 13:12:51 +000054 close $f
55
danielk1977ef317ab2004-06-23 10:43:10 +000056 set r [catch {
danielk1977f187bd92004-06-25 06:23:22 +000057 exec [file join . crashtest] crash.tcl >@stdout
danielk1977ef317ab2004-06-23 10:43:10 +000058 } msg]
59 lappend r $msg
danielk1977d1e3bee2004-06-22 13:12:51 +000060}
61
danielk1977ef317ab2004-06-23 10:43:10 +000062# The following procedure computes a "signature" for table "abc". If
63# abc changes in any way, the signature should change.
64proc signature {} {
65 return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
66}
67proc signature2 {} {
68 return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc2}]
69}
70
danielk1977ef317ab2004-06-23 10:43:10 +000071#--------------------------------------------------------------------------
danielk1977ece80f12004-06-23 01:05:26 +000072# Simple crash test:
73#
74# crash-1.1: Create a database with a table with two rows.
75# crash-1.2: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
danielk1977ef317ab2004-06-23 10:43:10 +000076# the first journal-sync.
danielk1977ece80f12004-06-23 01:05:26 +000077# crash-1.3: Ensure the database is in the same state as after crash-1.1.
78# crash-1.4: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
danielk1977ef317ab2004-06-23 10:43:10 +000079# the first database-sync.
danielk1977ece80f12004-06-23 01:05:26 +000080# crash-1.5: Ensure the database is in the same state as after crash-1.1.
danielk1977f187bd92004-06-25 06:23:22 +000081# crash-1.6: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
82# the second journal-sync.
83# crash-1.7: Ensure the database is in the same state as after crash-1.1.
danielk1977ece80f12004-06-23 01:05:26 +000084#
danielk1977f187bd92004-06-25 06:23:22 +000085# Tests 1.8 through 1.11 test for crashes on the third journal sync and
86# second database sync. Neither of these is required in such a small test
87# case, so these tests are just to verify that the test infrastructure
88# operates as expected.
danielk1977ef317ab2004-06-23 10:43:10 +000089#
danielk1977d1e3bee2004-06-22 13:12:51 +000090do_test crash-1.1 {
91 execsql {
92 CREATE TABLE abc(a, b, c);
93 INSERT INTO abc VALUES(1, 2, 3);
94 INSERT INTO abc VALUES(4, 5, 6);
95 }
danielk1977ef317ab2004-06-23 10:43:10 +000096 set ::sig [signature]
97 expr 0
98} {0}
danielk1977d1e3bee2004-06-22 13:12:51 +000099do_test crash-1.2 {
danielk1977ef317ab2004-06-23 10:43:10 +0000100 crashsql 1 test.db-journal {
101 DELETE FROM abc WHERE a = 1;
102 }
103} {1 {child process exited abnormally}}
danielk1977d1e3bee2004-06-22 13:12:51 +0000104do_test crash-1.3 {
danielk1977ef317ab2004-06-23 10:43:10 +0000105 signature
106} $::sig
danielk1977d1e3bee2004-06-22 13:12:51 +0000107do_test crash-1.4 {
danielk1977ef317ab2004-06-23 10:43:10 +0000108 crashsql 1 test.db {
109 DELETE FROM abc WHERE a = 1;
110 }
111} {1 {child process exited abnormally}}
danielk1977d1e3bee2004-06-22 13:12:51 +0000112do_test crash-1.5 {
danielk1977ef317ab2004-06-23 10:43:10 +0000113 signature
114} $::sig
115do_test crash-1.6 {
116 crashsql 2 test.db-journal {
117 DELETE FROM abc WHERE a = 1;
118 }
danielk1977f187bd92004-06-25 06:23:22 +0000119} {1 {child process exited abnormally}}
danielk1977ef317ab2004-06-23 10:43:10 +0000120do_test crash-1.7 {
danielk1977ece80f12004-06-23 01:05:26 +0000121 catchsql {
danielk1977d1e3bee2004-06-22 13:12:51 +0000122 SELECT * FROM abc;
123 }
danielk1977f187bd92004-06-25 06:23:22 +0000124} {0 {1 2 3 4 5 6}}
125
danielk1977ef317ab2004-06-23 10:43:10 +0000126do_test crash-1.8 {
danielk1977f187bd92004-06-25 06:23:22 +0000127 crashsql 3 test.db-journal {
128 DELETE FROM abc WHERE a = 1;
129 }
130} {0 {}}
131do_test crash-1.9 {
132 catchsql {
133 SELECT * FROM abc;
134 }
135} {0 {4 5 6}}
136do_test crash-1.10 {
danielk1977ef317ab2004-06-23 10:43:10 +0000137 crashsql 2 test.db {
138 DELETE FROM abc WHERE a = 4;
139 }
140} {0 {}}
danielk1977f187bd92004-06-25 06:23:22 +0000141do_test crash-1.11 {
danielk1977ef317ab2004-06-23 10:43:10 +0000142 catchsql {
143 SELECT * FROM abc;
144 }
145} {0 {}}
146
147#--------------------------------------------------------------------------
148# The following tests test recovery when both the database file and the the
149# journal file contain corrupt data. This can happen after pages are
150# written to the database file before a transaction is committed due to
151# cache-pressure.
152#
153# crash-2.1: Insert 18 pages of data into the database.
154# crash-2.2: Check the database file size looks ok.
155# crash-2.3: Delete 15 or so pages (with a 10 page page-cache), then crash.
156# crash-2.4: Ensure the database is in the same state as after crash-2.1.
157#
158# Test cases crash-2.5 and crash-2.6 check that the database is OK if the
159# crash occurs during the main database file sync. But this isn't really
160# different from the crash-1.* cases.
161#
162do_test crash-2.1 {
163 execsql { BEGIN }
164 for {set n 0} {$n < 1000} {incr n} {
165 execsql "INSERT INTO abc VALUES($n, [expr 2*$n], [expr 3*$n])"
166 }
167 execsql { COMMIT }
168 set ::sig [signature]
169 execsql { SELECT sum(a), sum(b), sum(c) from abc }
drh290c1942004-08-21 17:54:45 +0000170} {499500.0 999000.0 1498500.0}
danielk1977ef317ab2004-06-23 10:43:10 +0000171do_test crash-2.2 {
danielk1977ac11ee62005-01-15 12:45:51 +0000172 expr ([file size test.db] / 1024)>16
173} {1}
danielk1977ef317ab2004-06-23 10:43:10 +0000174do_test crash-2.3 {
175 crashsql 2 test.db-journal {
176 DELETE FROM abc WHERE a < 800;
177 }
178} {1 {child process exited abnormally}}
179do_test crash-2.4 {
180 signature
181} $sig
182do_test crash-2.5 {
183 crashsql 1 test.db {
184 DELETE FROM abc WHERE a<800;
185 }
186} {1 {child process exited abnormally}}
187do_test crash-2.6 {
188 signature
189} $sig
190
191#--------------------------------------------------------------------------
192# The crash-3.* test cases are essentially the same test as test case
193# crash-2.*, but with a more complicated data set.
194#
195# The test is repeated a few times with different seeds for the random
196# number generator in the crashing executable. Because there is no way to
197# seed the random number generator directly, some SQL is added to the test
198# case to 'use up' a different quantity random numbers before the test SQL
199# is executed.
200#
201
202# Make sure the file is much bigger than the pager-cache (10 pages). This
203# ensures that cache-spills happen regularly.
204do_test crash-3.0 {
205 execsql {
206 INSERT INTO abc SELECT * FROM abc;
207 INSERT INTO abc SELECT * FROM abc;
208 INSERT INTO abc SELECT * FROM abc;
209 INSERT INTO abc SELECT * FROM abc;
210 INSERT INTO abc SELECT * FROM abc;
211 }
danielk1977ac11ee62005-01-15 12:45:51 +0000212 expr ([file size test.db] / 1024) > 450
213} {1}
danielk1977ef317ab2004-06-23 10:43:10 +0000214for {set i 1} {$i < $repeats} {incr i} {
215 set sig [signature]
216 do_test crash-3.$i.1 {
217 crashsql [expr $i%5 + 1] test.db-journal "
218 BEGIN;
219 SELECT random() FROM abc LIMIT $i;
220 INSERT INTO abc VALUES(randstr(10,10), 0, 0);
221 DELETE FROM abc WHERE random()%10!=0;
222 COMMIT;
223 "
224 } {1 {child process exited abnormally}}
225 do_test crash-3.$i.2 {
226 signature
227 } $sig
228}
229
230#--------------------------------------------------------------------------
231# The following test cases - crash-4.* - test the correct recovery of the
232# database when a crash occurs during a multi-file transaction.
233#
234# crash-4.1.*: Test recovery when crash occurs during sync() of the
235# main database journal file.
236# crash-4.2.*: Test recovery when crash occurs during sync() of an
237# attached database journal file.
238# crash-4.3.*: Test recovery when crash occurs during sync() of the master
239# journal file.
240#
241do_test crash-4.0 {
242 file delete -force test2.db
243 file delete -force test2.db-journal
danielk1977ef317ab2004-06-23 10:43:10 +0000244 execsql {
245 ATTACH 'test2.db' AS aux;
danielk1977e35ee192004-06-26 09:50:11 +0000246 PRAGMA aux.default_cache_size = 10;
danielk1977ef317ab2004-06-23 10:43:10 +0000247 CREATE TABLE aux.abc2 AS SELECT 2*a as a, 2*b as b, 2*c as c FROM abc;
248 }
danielk1977ac11ee62005-01-15 12:45:51 +0000249 expr ([file size test2.db] / 1024) > 450
250} {1}
danielk1977ef317ab2004-06-23 10:43:10 +0000251
danielk19778191bff2004-06-28 04:52:30 +0000252for {set i 1} {$i<$repeats} {incr i} {
danielk1977ef317ab2004-06-23 10:43:10 +0000253 set sig [signature]
254 set sig2 [signature2]
255 do_test crash-4.1.$i.1 {
danielk19778191bff2004-06-28 04:52:30 +0000256 set c [crashsql $i test.db-journal "
danielk1977ef317ab2004-06-23 10:43:10 +0000257 ATTACH 'test2.db' AS aux;
258 BEGIN;
259 SELECT random() FROM abc LIMIT $i;
260 INSERT INTO abc VALUES(randstr(10,10), 0, 0);
261 DELETE FROM abc WHERE random()%10!=0;
262 INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
263 DELETE FROM abc2 WHERE random()%10!=0;
264 COMMIT;
danielk19778191bff2004-06-28 04:52:30 +0000265 "]
266 set c
danielk1977ef317ab2004-06-23 10:43:10 +0000267 } {1 {child process exited abnormally}}
268 do_test crash-4.1.$i.2 {
269 signature
270 } $sig
271 do_test crash-4.1.$i.3 {
272 signature2
273 } $sig2
274}
danielk19778191bff2004-06-28 04:52:30 +0000275set i 0
276while {[incr i]} {
danielk1977ef317ab2004-06-23 10:43:10 +0000277 set sig [signature]
278 set sig2 [signature2]
danielk19778191bff2004-06-28 04:52:30 +0000279 set ::fin 0
danielk1977ef317ab2004-06-23 10:43:10 +0000280 do_test crash-4.2.$i.1 {
danielk19778191bff2004-06-28 04:52:30 +0000281 set c [crashsql $i test2.db-journal "
danielk1977ef317ab2004-06-23 10:43:10 +0000282 ATTACH 'test2.db' AS aux;
283 BEGIN;
284 SELECT random() FROM abc LIMIT $i;
285 INSERT INTO abc VALUES(randstr(10,10), 0, 0);
286 DELETE FROM abc WHERE random()%10!=0;
287 INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
288 DELETE FROM abc2 WHERE random()%10!=0;
289 COMMIT;
danielk19778191bff2004-06-28 04:52:30 +0000290 "]
291 if { $c == {0 {}} } {
292 set ::fin 1
293 set c {1 {child process exited abnormally}}
294 }
295 set c
danielk1977ef317ab2004-06-23 10:43:10 +0000296 } {1 {child process exited abnormally}}
danielk19778191bff2004-06-28 04:52:30 +0000297 if { $::fin } break
danielk1977ef317ab2004-06-23 10:43:10 +0000298 do_test crash-4.2.$i.2 {
299 signature
300 } $sig
301 do_test crash-4.2.$i.3 {
302 signature2
303 } $sig2
304}
305for {set i 1} {$i < 5} {incr i} {
306 set sig [signature]
307 set sig2 [signature2]
308 do_test crash-4.3.$i.1 {
309 crashsql 1 test.db-mj* "
310 ATTACH 'test2.db' AS aux;
311 BEGIN;
312 SELECT random() FROM abc LIMIT $i;
313 INSERT INTO abc VALUES(randstr(10,10), 0, 0);
314 DELETE FROM abc WHERE random()%10!=0;
315 INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
316 DELETE FROM abc2 WHERE random()%10!=0;
317 COMMIT;
318 "
319 } {1 {child process exited abnormally}}
320 do_test crash-4.3.$i.2 {
321 signature
322 } $sig
323 do_test crash-4.3.$i.3 {
324 signature2
325 } $sig2
326}
danielk197794daf7f2004-11-08 09:26:09 +0000327
328#--------------------------------------------------------------------------
329# The following test cases - crash-5.* - exposes a bug that existed in the
330# sqlite3pager_movepage() API used by auto-vacuum databases.
331# database when a crash occurs during a multi-file transaction. See comments
332# in test crash-5.3 for details.
333#
334db close
335file delete -force test.db
336sqlite3 db test.db
337do_test crash-5.1 {
338 execsql {
339 CREATE TABLE abc(a, b, c); -- Root page 3
340 INSERT INTO abc VALUES(randstr(1500,1500), 0, 0); -- Overflow page 4
341 INSERT INTO abc SELECT * FROM abc;
342 INSERT INTO abc SELECT * FROM abc;
343 INSERT INTO abc SELECT * FROM abc;
344 }
345} {}
346do_test crash-5.2 {
347 expr [file size test.db] / 1024
danielk19774e17d142005-01-16 09:06:33 +0000348} [expr [string match [execsql {pragma auto_vacuum}] 1] ? 11 : 10]
danielk197794daf7f2004-11-08 09:26:09 +0000349set sig [signature]
350do_test crash-5.3 {
351# The SQL below is used to expose a bug that existed in
352# sqlite3pager_movepage() during development of the auto-vacuum feature. It
353# functions as follows:
354#
355# 1: Begin a transaction.
356# 2: Put page 4 on the free-list (was the overflow page for the row deleted).
357# 3: Write data to page 4 (it becomes the overflow page for the row inserted).
358# The old page 4 data has been written to the journal file, but the
359# journal file has not been sync()hronized.
360# 4: Create a table, which calls sqlite3pager_movepage() to move page 4
361# to the end of the database (page 12) to make room for the new root-page.
362# 5: Put pressure on the pager-cache. This results in page 4 being written
363# to the database file to make space in the cache to load a new page. The
364# bug was that page 4 was written to the database file before the journal
365# is sync()hronized.
366# 6: Commit. A crash occurs during the sync of the journal file.
367#
368# End result: Before the bug was fixed, data has been written to page 4 of the
369# database file and the journal file does not contain trustworthy rollback
370# data for this page.
371#
372 crashsql 1 test.db-journal {
373 BEGIN; -- 1
374 DELETE FROM abc WHERE oid = 1; -- 2
375 INSERT INTO abc VALUES(randstr(1500,1500), 0, 0); -- 3
376 CREATE TABLE abc2(a, b, c); -- 4
377 SELECT * FROM abc; -- 5
378 COMMIT; -- 6
379 }
380} {1 {child process exited abnormally}}
381integrity_check crash-5.4
382do_test crash-5.5 {
383 signature
384} $sig
385
danielk1977e0830e82004-11-08 09:51:09 +0000386#--------------------------------------------------------------------------
387# The following test cases - crash-6.* - test that a DROP TABLE operation
388# is correctly rolled back in the event of a crash while the database file
389# is being written. This is mainly to test that all pages are written to the
390# journal file before truncation in an auto-vacuum database.
391#
392do_test crash-6.1 {
393 crashsql 1 test.db {
394 DROP TABLE abc;
395 }
396} {1 {child process exited abnormally}}
397do_test crash-6.2 {
398 signature
399} $sig
danielk197794daf7f2004-11-08 09:26:09 +0000400
danielk1977aca790a2005-01-13 11:07:52 +0000401#--------------------------------------------------------------------------
402# These test cases test the case where the master journal file name is
403# corrupted slightly so that the corruption has to be detected by the
404# checksum.
405do_test crash-7.1 {
406 crashsql 1 test.db {
407 ATTACH 'test2.db' AS aux;
408 BEGIN;
409 INSERT INTO abc VALUES(randstr(1500,1500), 0, 0);
410 INSERT INTO abc2 VALUES(randstr(1500,1500), 0, 0);
411 COMMIT;
412 }
413
414 # Change the checksum value for the master journal name.
415 set f [open test.db-journal a]
416 fconfigure $f -encoding binary
417 seek $f [expr [file size test.db-journal] - 12]
418 puts -nonewline $f "\00\00\00\00"
419 close $f
420} {}
421do_test crash-7.2 {
422 signature
423} $sig
424
drh251b0672004-11-23 22:16:39 +0000425finish_test