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>