Annotation of embedaddon/sqlite3/test/crash.test, revision 1.1
1.1 ! misho 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 implements regression tests for SQLite library.
! 12: #
! 13: # 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 OS interface
! 16: # modules are overloaded using the modified I/O routines found in test6.c.
! 17: # These routines allow us to simulate the kind of file damage that
! 18: # occurs after a power failure.
! 19: #
! 20: # $Id: crash.test,v 1.27 2008/01/08 15:18:52 drh Exp $
! 21:
! 22: set testdir [file dirname $argv0]
! 23: source $testdir/tester.tcl
! 24:
! 25: ifcapable !crashtest {
! 26: finish_test
! 27: return
! 28: }
! 29:
! 30: set repeats 100
! 31: #set repeats 10
! 32:
! 33: # The following procedure computes a "signature" for table "abc". If
! 34: # abc changes in any way, the signature should change.
! 35: proc signature {} {
! 36: return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
! 37: }
! 38: proc signature2 {} {
! 39: return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc2}]
! 40: }
! 41:
! 42: #--------------------------------------------------------------------------
! 43: # Simple crash test:
! 44: #
! 45: # crash-1.1: Create a database with a table with two rows.
! 46: # crash-1.2: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
! 47: # the first journal-sync.
! 48: # crash-1.3: Ensure the database is in the same state as after crash-1.1.
! 49: # crash-1.4: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
! 50: # the first database-sync.
! 51: # crash-1.5: Ensure the database is in the same state as after crash-1.1.
! 52: # crash-1.6: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
! 53: # the second journal-sync.
! 54: # crash-1.7: Ensure the database is in the same state as after crash-1.1.
! 55: #
! 56: # Tests 1.8 through 1.11 test for crashes on the third journal sync and
! 57: # second database sync. Neither of these is required in such a small test
! 58: # case, so these tests are just to verify that the test infrastructure
! 59: # operates as expected.
! 60: #
! 61: do_test crash-1.1 {
! 62: execsql {
! 63: CREATE TABLE abc(a, b, c);
! 64: INSERT INTO abc VALUES(1, 2, 3);
! 65: INSERT INTO abc VALUES(4, 5, 6);
! 66: }
! 67: set ::sig [signature]
! 68: expr 0
! 69: } {0}
! 70: for {set i 0} {$i<10} {incr i} {
! 71: set seed [expr {int(abs(rand()*10000))}]
! 72: do_test crash-1.2.$i {
! 73: crashsql -delay 1 -file test.db-journal -seed $seed {
! 74: DELETE FROM abc WHERE a = 1;
! 75: }
! 76: } {1 {child process exited abnormally}}
! 77: do_test crash-1.3.$i {
! 78: signature
! 79: } $::sig
! 80: }
! 81: do_test crash-1.4 {
! 82: crashsql -delay 1 -file test.db {
! 83: DELETE FROM abc WHERE a = 1;
! 84: }
! 85: } {1 {child process exited abnormally}}
! 86: do_test crash-1.5 {
! 87: signature
! 88: } $::sig
! 89: do_test crash-1.6 {
! 90: crashsql -delay 2 -file test.db-journal {
! 91: DELETE FROM abc WHERE a = 1;
! 92: }
! 93: } {1 {child process exited abnormally}}
! 94: do_test crash-1.7 {
! 95: catchsql {
! 96: SELECT * FROM abc;
! 97: }
! 98: } {0 {1 2 3 4 5 6}}
! 99:
! 100: do_test crash-1.8 {
! 101: crashsql -delay 3 -file test.db-journal {
! 102: DELETE FROM abc WHERE a = 1;
! 103: }
! 104: } {0 {}}
! 105: do_test crash-1.9 {
! 106: catchsql {
! 107: SELECT * FROM abc;
! 108: }
! 109: } {0 {4 5 6}}
! 110: do_test crash-1.10 {
! 111: crashsql -delay 2 -file test.db {
! 112: DELETE FROM abc WHERE a = 4;
! 113: }
! 114: } {0 {}}
! 115: do_test crash-1.11 {
! 116: catchsql {
! 117: SELECT * FROM abc;
! 118: }
! 119: } {0 {}}
! 120:
! 121: #--------------------------------------------------------------------------
! 122: # The following tests test recovery when both the database file and the the
! 123: # journal file contain corrupt data. This can happen after pages are
! 124: # written to the database file before a transaction is committed due to
! 125: # cache-pressure.
! 126: #
! 127: # crash-2.1: Insert 18 pages of data into the database.
! 128: # crash-2.2: Check the database file size looks ok.
! 129: # crash-2.3: Delete 15 or so pages (with a 10 page page-cache), then crash.
! 130: # crash-2.4: Ensure the database is in the same state as after crash-2.1.
! 131: #
! 132: # Test cases crash-2.5 and crash-2.6 check that the database is OK if the
! 133: # crash occurs during the main database file sync. But this isn't really
! 134: # different from the crash-1.* cases.
! 135: #
! 136: do_test crash-2.1 {
! 137: execsql { BEGIN }
! 138: for {set n 0} {$n < 1000} {incr n} {
! 139: execsql "INSERT INTO abc VALUES($n, [expr 2*$n], [expr 3*$n])"
! 140: }
! 141: execsql { COMMIT }
! 142: set ::sig [signature]
! 143: execsql { SELECT sum(a), sum(b), sum(c) from abc }
! 144: } {499500 999000 1498500}
! 145: do_test crash-2.2 {
! 146: expr ([file size test.db] / 1024)>16
! 147: } {1}
! 148: do_test crash-2.3 {
! 149: crashsql -delay 2 -file test.db-journal {
! 150: DELETE FROM abc WHERE a < 800;
! 151: }
! 152: } {1 {child process exited abnormally}}
! 153: do_test crash-2.4 {
! 154: signature
! 155: } $sig
! 156: do_test crash-2.5 {
! 157: crashsql -delay 1 -file test.db {
! 158: DELETE FROM abc WHERE a<800;
! 159: }
! 160: } {1 {child process exited abnormally}}
! 161: do_test crash-2.6 {
! 162: signature
! 163: } $sig
! 164:
! 165: #--------------------------------------------------------------------------
! 166: # The crash-3.* test cases are essentially the same test as test case
! 167: # crash-2.*, but with a more complicated data set.
! 168: #
! 169: # The test is repeated a few times with different seeds for the random
! 170: # number generator in the crashing executable. Because there is no way to
! 171: # seed the random number generator directly, some SQL is added to the test
! 172: # case to 'use up' a different quantity random numbers before the test SQL
! 173: # is executed.
! 174: #
! 175:
! 176: # Make sure the file is much bigger than the pager-cache (10 pages). This
! 177: # ensures that cache-spills happen regularly.
! 178: do_test crash-3.0 {
! 179: execsql {
! 180: INSERT INTO abc SELECT * FROM abc;
! 181: INSERT INTO abc SELECT * FROM abc;
! 182: INSERT INTO abc SELECT * FROM abc;
! 183: INSERT INTO abc SELECT * FROM abc;
! 184: INSERT INTO abc SELECT * FROM abc;
! 185: }
! 186: expr ([file size test.db] / 1024) > 450
! 187: } {1}
! 188: for {set i 1} {$i < $repeats} {incr i} {
! 189: set sig [signature]
! 190: do_test crash-3.$i.1 {
! 191: set seed [expr {int(abs(rand()*10000))}]
! 192: crashsql -delay [expr $i%5 + 1] -file test.db-journal -seed $seed "
! 193: BEGIN;
! 194: SELECT random() FROM abc LIMIT $i;
! 195: INSERT INTO abc VALUES(randstr(10,10), 0, 0);
! 196: DELETE FROM abc WHERE random()%10!=0;
! 197: COMMIT;
! 198: "
! 199: } {1 {child process exited abnormally}}
! 200: do_test crash-3.$i.2 {
! 201: signature
! 202: } $sig
! 203: }
! 204:
! 205: #--------------------------------------------------------------------------
! 206: # The following test cases - crash-4.* - test the correct recovery of the
! 207: # database when a crash occurs during a multi-file transaction.
! 208: #
! 209: # crash-4.1.*: Test recovery when crash occurs during sync() of the
! 210: # main database journal file.
! 211: # crash-4.2.*: Test recovery when crash occurs during sync() of an
! 212: # attached database journal file.
! 213: # crash-4.3.*: Test recovery when crash occurs during sync() of the master
! 214: # journal file.
! 215: #
! 216: ifcapable attach {
! 217: do_test crash-4.0 {
! 218: forcedelete test2.db
! 219: forcedelete test2.db-journal
! 220: execsql {
! 221: ATTACH 'test2.db' AS aux;
! 222: PRAGMA aux.default_cache_size = 10;
! 223: CREATE TABLE aux.abc2 AS SELECT 2*a as a, 2*b as b, 2*c as c FROM abc;
! 224: }
! 225: expr ([file size test2.db] / 1024) > 450
! 226: } {1}
! 227:
! 228: set fin 0
! 229: for {set i 1} {$i<$repeats} {incr i} {
! 230: set seed [expr {int(abs(rand()*10000))}]
! 231: set sig [signature]
! 232: set sig2 [signature2]
! 233: do_test crash-4.1.$i.1 {
! 234: set c [crashsql -delay $i -file test.db-journal -seed $::seed "
! 235: ATTACH 'test2.db' AS aux;
! 236: BEGIN;
! 237: SELECT randstr($i,$i) FROM abc LIMIT $i;
! 238: INSERT INTO abc VALUES(randstr(10,10), 0, 0);
! 239: DELETE FROM abc WHERE random()%10!=0;
! 240: INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
! 241: DELETE FROM abc2 WHERE random()%10!=0;
! 242: COMMIT;
! 243: "]
! 244: if { $c == {0 {}} } {
! 245: set ::fin 1
! 246: set c {1 {child process exited abnormally}}
! 247: }
! 248: set c
! 249: } {1 {child process exited abnormally}}
! 250: if {$::fin} break
! 251: do_test crash-4.1.$i.2 {
! 252: signature
! 253: } $sig
! 254: do_test crash-4.1.$i.3 {
! 255: signature2
! 256: } $sig2
! 257: }
! 258: set i 0
! 259: set fin 0
! 260: while {[incr i]} {
! 261: set seed [expr {int(abs(rand()*10000))}]
! 262: set sig [signature]
! 263: set sig2 [signature2]
! 264: set ::fin 0
! 265: do_test crash-4.2.$i.1 {
! 266: set c [crashsql -delay $i -file test2.db-journal -seed $::seed "
! 267: ATTACH 'test2.db' AS aux;
! 268: BEGIN;
! 269: SELECT randstr($i,$i) FROM abc LIMIT $i;
! 270: INSERT INTO abc VALUES(randstr(10,10), 0, 0);
! 271: DELETE FROM abc WHERE random()%10!=0;
! 272: INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
! 273: DELETE FROM abc2 WHERE random()%10!=0;
! 274: COMMIT;
! 275: "]
! 276: if { $c == {0 {}} } {
! 277: set ::fin 1
! 278: set c {1 {child process exited abnormally}}
! 279: }
! 280: set c
! 281: } {1 {child process exited abnormally}}
! 282: if { $::fin } break
! 283: do_test crash-4.2.$i.2 {
! 284: signature
! 285: } $sig
! 286: do_test crash-4.2.$i.3 {
! 287: signature2
! 288: } $sig2
! 289: }
! 290: for {set i 1} {$i < 5} {incr i} {
! 291: set sig [signature]
! 292: set sig2 [signature2]
! 293: do_test crash-4.3.$i.1 {
! 294: crashsql -delay 1 -file test.db-mj* "
! 295: ATTACH 'test2.db' AS aux;
! 296: BEGIN;
! 297: SELECT random() FROM abc LIMIT $i;
! 298: INSERT INTO abc VALUES(randstr(10,10), 0, 0);
! 299: DELETE FROM abc WHERE random()%10!=0;
! 300: INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
! 301: DELETE FROM abc2 WHERE random()%10!=0;
! 302: COMMIT;
! 303: "
! 304: } {1 {child process exited abnormally}}
! 305: do_test crash-4.3.$i.2 {
! 306: signature
! 307: } $sig
! 308: do_test crash-4.3.$i.3 {
! 309: signature2
! 310: } $sig2
! 311: }
! 312: }
! 313:
! 314: #--------------------------------------------------------------------------
! 315: # The following test cases - crash-5.* - exposes a bug that existed in the
! 316: # sqlite3pager_movepage() API used by auto-vacuum databases.
! 317: # database when a crash occurs during a multi-file transaction. See comments
! 318: # in test crash-5.3 for details.
! 319: #
! 320: db close
! 321: forcedelete test.db
! 322: sqlite3 db test.db
! 323: do_test crash-5.1 {
! 324: execsql {
! 325: CREATE TABLE abc(a, b, c); -- Root page 3
! 326: INSERT INTO abc VALUES(randstr(1500,1500), 0, 0); -- Overflow page 4
! 327: INSERT INTO abc SELECT * FROM abc;
! 328: INSERT INTO abc SELECT * FROM abc;
! 329: INSERT INTO abc SELECT * FROM abc;
! 330: }
! 331: } {}
! 332: do_test crash-5.2 {
! 333: expr [file size test.db] / 1024
! 334: } [expr [string match [execsql {pragma auto_vacuum}] 1] ? 11 : 10]
! 335: set sig [signature]
! 336: do_test crash-5.3 {
! 337: # The SQL below is used to expose a bug that existed in
! 338: # sqlite3pager_movepage() during development of the auto-vacuum feature. It
! 339: # functions as follows:
! 340: #
! 341: # 1: Begin a transaction.
! 342: # 2: Put page 4 on the free-list (was the overflow page for the row deleted).
! 343: # 3: Write data to page 4 (it becomes the overflow page for the row inserted).
! 344: # The old page 4 data has been written to the journal file, but the
! 345: # journal file has not been sync()hronized.
! 346: # 4: Create a table, which calls sqlite3pager_movepage() to move page 4
! 347: # to the end of the database (page 12) to make room for the new root-page.
! 348: # 5: Put pressure on the pager-cache. This results in page 4 being written
! 349: # to the database file to make space in the cache to load a new page. The
! 350: # bug was that page 4 was written to the database file before the journal
! 351: # is sync()hronized.
! 352: # 6: Commit. A crash occurs during the sync of the journal file.
! 353: #
! 354: # End result: Before the bug was fixed, data has been written to page 4 of the
! 355: # database file and the journal file does not contain trustworthy rollback
! 356: # data for this page.
! 357: #
! 358: crashsql -delay 1 -file test.db-journal {
! 359: BEGIN; -- 1
! 360: DELETE FROM abc WHERE oid = 1; -- 2
! 361: INSERT INTO abc VALUES(randstr(1500,1500), 0, 0); -- 3
! 362: CREATE TABLE abc2(a, b, c); -- 4
! 363: SELECT * FROM abc; -- 5
! 364: COMMIT; -- 6
! 365: }
! 366: } {1 {child process exited abnormally}}
! 367: integrity_check crash-5.4
! 368: do_test crash-5.5 {
! 369: signature
! 370: } $sig
! 371:
! 372: #--------------------------------------------------------------------------
! 373: # The following test cases - crash-6.* - test that a DROP TABLE operation
! 374: # is correctly rolled back in the event of a crash while the database file
! 375: # is being written. This is mainly to test that all pages are written to the
! 376: # journal file before truncation in an auto-vacuum database.
! 377: #
! 378: do_test crash-6.1 {
! 379: crashsql -delay 1 -file test.db {
! 380: DROP TABLE abc;
! 381: }
! 382: } {1 {child process exited abnormally}}
! 383: do_test crash-6.2 {
! 384: signature
! 385: } $sig
! 386:
! 387: #--------------------------------------------------------------------------
! 388: # These test cases test the case where the master journal file name is
! 389: # corrupted slightly so that the corruption has to be detected by the
! 390: # checksum.
! 391: do_test crash-7.1 {
! 392: crashsql -delay 1 -file test.db {
! 393: ATTACH 'test2.db' AS aux;
! 394: BEGIN;
! 395: INSERT INTO abc VALUES(randstr(1500,1500), 0, 0);
! 396: INSERT INTO abc2 VALUES(randstr(1500,1500), 0, 0);
! 397: COMMIT;
! 398: }
! 399:
! 400: # Change the checksum value for the master journal name.
! 401: set f [open test.db-journal a]
! 402: fconfigure $f -encoding binary
! 403: seek $f [expr [file size test.db-journal] - 12]
! 404: puts -nonewline $f "\00\00\00\00"
! 405: close $f
! 406: } {}
! 407: do_test crash-7.2 {
! 408: signature
! 409: } $sig
! 410:
! 411: finish_test
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>