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>