Annotation of embedaddon/sqlite3/test/crash8.test, revision 1.1
1.1 ! misho 1: # 2009 January 8
! 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: #
! 12: # This test verifies a couple of specific potential data corruption
! 13: # scenarios involving crashes or power failures.
! 14: #
! 15: # Later: Also, some other specific scenarios required for coverage
! 16: # testing that do not lead to corruption.
! 17: #
! 18: # $Id: crash8.test,v 1.4 2009/01/11 00:44:48 drh Exp $
! 19:
! 20:
! 21: set testdir [file dirname $argv0]
! 22: source $testdir/tester.tcl
! 23:
! 24: ifcapable !crashtest {
! 25: finish_test
! 26: return
! 27: }
! 28:
! 29: do_test crash8-1.1 {
! 30: execsql {
! 31: PRAGMA auto_vacuum=OFF;
! 32: CREATE TABLE t1(a, b);
! 33: CREATE INDEX i1 ON t1(a, b);
! 34: INSERT INTO t1 VALUES(1, randstr(1000,1000));
! 35: INSERT INTO t1 VALUES(2, randstr(1000,1000));
! 36: INSERT INTO t1 VALUES(3, randstr(1000,1000));
! 37: INSERT INTO t1 VALUES(4, randstr(1000,1000));
! 38: INSERT INTO t1 VALUES(5, randstr(1000,1000));
! 39: INSERT INTO t1 VALUES(6, randstr(1000,1000));
! 40: CREATE TABLE t2(a, b);
! 41: CREATE TABLE t3(a, b);
! 42: CREATE TABLE t4(a, b);
! 43: CREATE TABLE t5(a, b);
! 44: CREATE TABLE t6(a, b);
! 45: CREATE TABLE t7(a, b);
! 46: CREATE TABLE t8(a, b);
! 47: CREATE TABLE t9(a, b);
! 48: CREATE TABLE t10(a, b);
! 49: PRAGMA integrity_check
! 50: }
! 51: } {ok}
! 52:
! 53:
! 54: # Potential corruption scenario 1. A second process opens the database
! 55: # and modifies a large portion of it. It then opens a second transaction
! 56: # and modifies a small part of the database, but crashes before it commits
! 57: # the transaction.
! 58: #
! 59: # When the first process accessed the database again, it was rolling back
! 60: # the aborted transaction, but was not purging its in-memory cache (which
! 61: # was loaded before the second process made its first, successful,
! 62: # modification). Producing an inconsistent cache.
! 63: #
! 64: do_test crash8-1.2 {
! 65: crashsql -delay 2 -file test.db {
! 66: PRAGMA cache_size = 10;
! 67: UPDATE t1 SET b = randstr(1000,1000);
! 68: INSERT INTO t9 VALUES(1, 2);
! 69: }
! 70: } {1 {child process exited abnormally}}
! 71: do_test crash8-1.3 {
! 72: execsql {PRAGMA integrity_check}
! 73: } {ok}
! 74:
! 75: # Potential corruption scenario 2. The second process, operating in
! 76: # persistent-journal mode, makes a large change to the database file
! 77: # with a small in-memory cache. Such that more than one journal-header
! 78: # was written to the file. It then opens a second transaction and makes
! 79: # a smaller change that requires only a single journal-header to be
! 80: # written to the journal file. The second change is such that the
! 81: # journal content written to the persistent journal file exactly overwrites
! 82: # the first journal-header and set of subsequent records written by the
! 83: # first, successful, change. The second process crashes before it can
! 84: # commit its second change.
! 85: #
! 86: # When the first process accessed the database again, it was rolling back
! 87: # the second aborted transaction, then continuing to rollback the second
! 88: # and subsequent journal-headers written by the first, successful, change.
! 89: # Database corruption.
! 90: #
! 91: do_test crash8.2.1 {
! 92: crashsql -delay 2 -file test.db {
! 93: PRAGMA journal_mode = persist;
! 94: PRAGMA cache_size = 10;
! 95: UPDATE t1 SET b = randstr(1000,1000);
! 96: PRAGMA cache_size = 100;
! 97: BEGIN;
! 98: INSERT INTO t2 VALUES('a', 'b');
! 99: INSERT INTO t3 VALUES('a', 'b');
! 100: INSERT INTO t4 VALUES('a', 'b');
! 101: INSERT INTO t5 VALUES('a', 'b');
! 102: INSERT INTO t6 VALUES('a', 'b');
! 103: INSERT INTO t7 VALUES('a', 'b');
! 104: INSERT INTO t8 VALUES('a', 'b');
! 105: INSERT INTO t9 VALUES('a', 'b');
! 106: INSERT INTO t10 VALUES('a', 'b');
! 107: COMMIT;
! 108: }
! 109: } {1 {child process exited abnormally}}
! 110:
! 111: do_test crash8-2.3 {
! 112: execsql {PRAGMA integrity_check}
! 113: } {ok}
! 114:
! 115: proc read_file {zFile} {
! 116: set fd [open $zFile]
! 117: fconfigure $fd -translation binary
! 118: set zData [read $fd]
! 119: close $fd
! 120: return $zData
! 121: }
! 122: proc write_file {zFile zData} {
! 123: set fd [open $zFile w]
! 124: fconfigure $fd -translation binary
! 125: puts -nonewline $fd $zData
! 126: close $fd
! 127: }
! 128:
! 129: # The following tests check that SQLite will not roll back a hot-journal
! 130: # file if the sector-size field in the first journal file header is
! 131: # suspect. Definition of suspect:
! 132: #
! 133: # a) Not a power of 2, or (crash8-3.5)
! 134: # b) Greater than 0x01000000 (16MB), or (crash8-3.6)
! 135: # c) Less than 512. (crash8-3.7)
! 136: #
! 137: # Also test that SQLite will not rollback a hot-journal file with a
! 138: # suspect page-size. In this case "suspect" means:
! 139: #
! 140: # a) Not a power of 2, or
! 141: # b) Less than 512, or
! 142: # c) Greater than SQLITE_MAX_PAGE_SIZE
! 143: #
! 144: do_test crash8-3.1 {
! 145: list [file exists test.db-joural] [file exists test.db]
! 146: } {0 1}
! 147: do_test crash8-3.2 {
! 148: execsql {
! 149: PRAGMA synchronous = off;
! 150: BEGIN;
! 151: DELETE FROM t1;
! 152: SELECT count(*) FROM t1;
! 153: }
! 154: } {0}
! 155: do_test crash8-3.3 {
! 156: set zJournal [read_file test.db-journal]
! 157: execsql {
! 158: COMMIT;
! 159: SELECT count(*) FROM t1;
! 160: }
! 161: } {0}
! 162: do_test crash8-3.4 {
! 163: binary scan [string range $zJournal 20 23] I nSector
! 164: set nSector
! 165: } {512}
! 166:
! 167: do_test crash8-3.5 {
! 168: set zJournal2 [string replace $zJournal 20 23 [binary format I 513]]
! 169: write_file test.db-journal $zJournal2
! 170:
! 171: execsql {
! 172: SELECT count(*) FROM t1;
! 173: PRAGMA integrity_check
! 174: }
! 175: } {0 ok}
! 176: do_test crash8-3.6 {
! 177: set zJournal2 [string replace $zJournal 20 23 [binary format I 0x2000000]]
! 178: write_file test.db-journal $zJournal2
! 179: execsql {
! 180: SELECT count(*) FROM t1;
! 181: PRAGMA integrity_check
! 182: }
! 183: } {0 ok}
! 184: do_test crash8-3.7 {
! 185: set zJournal2 [string replace $zJournal 20 23 [binary format I 256]]
! 186: write_file test.db-journal $zJournal2
! 187: execsql {
! 188: SELECT count(*) FROM t1;
! 189: PRAGMA integrity_check
! 190: }
! 191: } {0 ok}
! 192:
! 193: do_test crash8-3.8 {
! 194: set zJournal2 [string replace $zJournal 24 27 [binary format I 513]]
! 195: write_file test.db-journal $zJournal2
! 196:
! 197: execsql {
! 198: SELECT count(*) FROM t1;
! 199: PRAGMA integrity_check
! 200: }
! 201: } {0 ok}
! 202: do_test crash8-3.9 {
! 203: set big [expr $SQLITE_MAX_PAGE_SIZE * 2]
! 204: set zJournal2 [string replace $zJournal 24 27 [binary format I $big]]
! 205: write_file test.db-journal $zJournal2
! 206: execsql {
! 207: SELECT count(*) FROM t1;
! 208: PRAGMA integrity_check
! 209: }
! 210: } {0 ok}
! 211: do_test crash8-3.10 {
! 212: set zJournal2 [string replace $zJournal 24 27 [binary format I 256]]
! 213: write_file test.db-journal $zJournal2
! 214: execsql {
! 215: SELECT count(*) FROM t1;
! 216: PRAGMA integrity_check
! 217: }
! 218: } {0 ok}
! 219:
! 220: do_test crash8-3.11 {
! 221: set fd [open test.db-journal w]
! 222: fconfigure $fd -translation binary
! 223: puts -nonewline $fd $zJournal
! 224: close $fd
! 225: execsql {
! 226: SELECT count(*) FROM t1;
! 227: PRAGMA integrity_check
! 228: }
! 229: } {6 ok}
! 230:
! 231:
! 232: # If a connection running in persistent-journal mode is part of a
! 233: # multi-file transaction, it must ensure that the master-journal name
! 234: # appended to the journal file contents during the commit is located
! 235: # at the end of the physical journal file. If there was already a
! 236: # large journal file allocated at the start of the transaction, this
! 237: # may mean truncating the file so that the master journal name really
! 238: # is at the physical end of the file.
! 239: #
! 240: # This block of tests test that SQLite correctly truncates such
! 241: # journal files, and that the results behave correctly if a hot-journal
! 242: # rollback occurs.
! 243: #
! 244: ifcapable pragma {
! 245: reset_db
! 246: forcedelete test2.db
! 247:
! 248: do_test crash8-4.1 {
! 249: execsql {
! 250: PRAGMA journal_mode = persist;
! 251: CREATE TABLE ab(a, b);
! 252: INSERT INTO ab VALUES(0, 'abc');
! 253: INSERT INTO ab VALUES(1, NULL);
! 254: INSERT INTO ab VALUES(2, NULL);
! 255: INSERT INTO ab VALUES(3, NULL);
! 256: INSERT INTO ab VALUES(4, NULL);
! 257: INSERT INTO ab VALUES(5, NULL);
! 258: INSERT INTO ab VALUES(6, NULL);
! 259: UPDATE ab SET b = randstr(1000,1000);
! 260: ATTACH 'test2.db' AS aux;
! 261: PRAGMA aux.journal_mode = persist;
! 262: CREATE TABLE aux.ab(a, b);
! 263: INSERT INTO aux.ab SELECT * FROM main.ab;
! 264:
! 265: UPDATE aux.ab SET b = randstr(1000,1000) WHERE a>=1;
! 266: UPDATE ab SET b = randstr(1000,1000) WHERE a>=1;
! 267: }
! 268: list [file exists test.db-journal] [file exists test2.db-journal]
! 269: } {1 1}
! 270:
! 271: do_test crash8-4.2 {
! 272: execsql {
! 273: BEGIN;
! 274: UPDATE aux.ab SET b = 'def' WHERE a = 0;
! 275: UPDATE main.ab SET b = 'def' WHERE a = 0;
! 276: COMMIT;
! 277: }
! 278: } {}
! 279:
! 280: do_test crash8-4.3 {
! 281: execsql {
! 282: UPDATE aux.ab SET b = randstr(1000,1000) WHERE a>=1;
! 283: UPDATE ab SET b = randstr(1000,1000) WHERE a>=1;
! 284: }
! 285: } {}
! 286:
! 287: set contents_main [db eval {SELECT b FROM main.ab WHERE a = 1}]
! 288: set contents_aux [db eval {SELECT b FROM aux.ab WHERE a = 1}]
! 289:
! 290: do_test crash8-4.4 {
! 291: crashsql -file test2.db -delay 1 {
! 292: ATTACH 'test2.db' AS aux;
! 293: BEGIN;
! 294: UPDATE aux.ab SET b = 'ghi' WHERE a = 0;
! 295: UPDATE main.ab SET b = 'ghi' WHERE a = 0;
! 296: COMMIT;
! 297: }
! 298: } {1 {child process exited abnormally}}
! 299:
! 300: do_test crash8-4.5 {
! 301: list [file exists test.db-journal] [file exists test2.db-journal]
! 302: } {1 1}
! 303:
! 304: do_test crash8-4.6 {
! 305: execsql {
! 306: SELECT b FROM main.ab WHERE a = 0;
! 307: SELECT b FROM aux.ab WHERE a = 0;
! 308: }
! 309: } {def def}
! 310:
! 311: do_test crash8-4.7 {
! 312: crashsql -file test2.db -delay 1 {
! 313: ATTACH 'test2.db' AS aux;
! 314: BEGIN;
! 315: UPDATE aux.ab SET b = 'jkl' WHERE a = 0;
! 316: UPDATE main.ab SET b = 'jkl' WHERE a = 0;
! 317: COMMIT;
! 318: }
! 319: } {1 {child process exited abnormally}}
! 320:
! 321: do_test crash8-4.8 {
! 322: set fd [open test.db-journal]
! 323: fconfigure $fd -translation binary
! 324: seek $fd -16 end
! 325: binary scan [read $fd 4] I len
! 326:
! 327: seek $fd [expr {-1 * ($len + 16)}] end
! 328: set zMasterJournal [read $fd $len]
! 329: close $fd
! 330:
! 331: file exists $zMasterJournal
! 332: } {1}
! 333:
! 334: do_test crash8-4.9 {
! 335: execsql { SELECT b FROM aux.ab WHERE a = 0 }
! 336: } {def}
! 337:
! 338: do_test crash8-4.10 {
! 339: delete_file $zMasterJournal
! 340: execsql { SELECT b FROM main.ab WHERE a = 0 }
! 341: } {jkl}
! 342: }
! 343:
! 344: for {set i 1} {$i < 10} {incr i} {
! 345: catch { db close }
! 346: forcedelete test.db test.db-journal
! 347: sqlite3 db test.db
! 348: do_test crash8-5.$i.1 {
! 349: execsql {
! 350: CREATE TABLE t1(x PRIMARY KEY);
! 351: INSERT INTO t1 VALUES(randomblob(900));
! 352: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 353: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 354: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 355: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 356: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 357: INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */
! 358: }
! 359: crashsql -file test.db -delay [expr ($::i%2) + 1] {
! 360: PRAGMA cache_size = 10;
! 361: BEGIN;
! 362: UPDATE t1 SET x = randomblob(900);
! 363: ROLLBACK;
! 364: INSERT INTO t1 VALUES(randomblob(900));
! 365: }
! 366: execsql { PRAGMA integrity_check }
! 367: } {ok}
! 368:
! 369: catch { db close }
! 370: forcedelete test.db test.db-journal
! 371: sqlite3 db test.db
! 372: do_test crash8-5.$i.2 {
! 373: execsql {
! 374: PRAGMA cache_size = 10;
! 375: CREATE TABLE t1(x PRIMARY KEY);
! 376: INSERT INTO t1 VALUES(randomblob(900));
! 377: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 378: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 379: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 380: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 381: INSERT INTO t1 SELECT randomblob(900) FROM t1;
! 382: INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */
! 383: BEGIN;
! 384: UPDATE t1 SET x = randomblob(900);
! 385: }
! 386: forcedelete testX.db testX.db-journal testX.db-wal
! 387: forcecopy test.db testX.db
! 388: forcecopy test.db-journal testX.db-journal
! 389: db close
! 390:
! 391: crashsql -file test.db -delay [expr ($::i%2) + 1] {
! 392: SELECT * FROM sqlite_master;
! 393: INSERT INTO t1 VALUES(randomblob(900));
! 394: }
! 395:
! 396: sqlite3 db2 testX.db
! 397: execsql { PRAGMA integrity_check } db2
! 398: } {ok}
! 399: }
! 400: catch {db2 close}
! 401:
! 402: finish_test
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>