File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sqlite3 / test / crash8.test
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 17:04:16 2012 UTC (12 years, 4 months ago) by misho
Branches: sqlite3, MAIN
CVS tags: v3_7_10, HEAD
sqlite3

    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.1.1.1 2012/02/21 17:04:16 misho 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>