Annotation of embedaddon/sqlite3/test/walcksum.test, revision 1.1

1.1     ! misho       1: # 2010 May 24
        !             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: 
        !            13: set testdir [file dirname $argv0]
        !            14: source $testdir/tester.tcl
        !            15: source $testdir/lock_common.tcl
        !            16: source $testdir/wal_common.tcl
        !            17: 
        !            18: ifcapable !wal {finish_test ; return }
        !            19: 
        !            20: # Read and return the contents of file $filename. Treat the content as
        !            21: # binary data.
        !            22: #
        !            23: proc readfile {filename} {
        !            24:   set fd [open $filename]
        !            25:   fconfigure $fd -encoding binary
        !            26:   fconfigure $fd -translation binary
        !            27:   set data [read $fd]
        !            28:   close $fd
        !            29:   return $data
        !            30: }
        !            31: 
        !            32: #
        !            33: # File $filename must be a WAL file on disk. Check that the checksum of frame
        !            34: # $iFrame in the file is correct when interpreting data as $endian-endian
        !            35: # integers ($endian must be either "big" or "little"). If the checksum looks
        !            36: # correct, return 1. Otherwise 0.
        !            37: #
        !            38: proc log_checksum_verify {filename iFrame endian} {
        !            39:   set data [readfile $filename]
        !            40: 
        !            41:   foreach {offset c1 c2} [log_checksum_calc $data $iFrame $endian] {}
        !            42: 
        !            43:   binary scan [string range $data $offset [expr $offset+7]] II expect1 expect2
        !            44:   set expect1 [expr $expect1&0xFFFFFFFF]
        !            45:   set expect2 [expr $expect2&0xFFFFFFFF]
        !            46: 
        !            47:   expr {$c1==$expect1 && $c2==$expect2}
        !            48: }
        !            49: 
        !            50: # File $filename must be a WAL file on disk. Compute the checksum for frame
        !            51: # $iFrame in the file by interpreting data as $endian-endian integers 
        !            52: # ($endian must be either "big" or "little"). Then write the computed 
        !            53: # checksum into the file.
        !            54: #
        !            55: proc log_checksum_write {filename iFrame endian} {
        !            56:   set data [readfile $filename]
        !            57: 
        !            58:   foreach {offset c1 c2} [log_checksum_calc $data $iFrame $endian] {}
        !            59: 
        !            60:   set bin [binary format II $c1 $c2]
        !            61:   set fd [open $filename r+]
        !            62:   fconfigure $fd -encoding binary
        !            63:   fconfigure $fd -translation binary
        !            64:   seek $fd $offset
        !            65:   puts -nonewline $fd $bin
        !            66:   close $fd
        !            67: }
        !            68: 
        !            69: # Calculate and return the checksum for a particular frame in a WAL.
        !            70: #
        !            71: # Arguments are:
        !            72: #
        !            73: #   $data         Blob containing the entire contents of a WAL.
        !            74: #
        !            75: #   $iFrame       Frame number within the $data WAL. Frames are numbered 
        !            76: #                 starting at 1.
        !            77: #
        !            78: #   $endian       One of "big" or "little".
        !            79: #
        !            80: # Returns a list of three elements, as follows:
        !            81: #
        !            82: #   * The byte offset of the checksum belonging to frame $iFrame in the WAL.
        !            83: #   * The first integer in the calculated version of the checksum.
        !            84: #   * The second integer in the calculated version of the checksum.
        !            85: #
        !            86: proc log_checksum_calc {data iFrame endian} {
        !            87:   
        !            88:   binary scan [string range $data 8 11] I pgsz
        !            89:   if {$iFrame > 1} {
        !            90:     set n [wal_file_size [expr $iFrame-2] $pgsz]
        !            91:     binary scan [string range $data [expr $n+16] [expr $n+23]] II c1 c2
        !            92:   } else {
        !            93:     set c1 0
        !            94:     set c2 0
        !            95:     wal_cksum $endian c1 c2 [string range $data 0 23]
        !            96:   }
        !            97: 
        !            98:   set n [wal_file_size [expr $iFrame-1] $pgsz]
        !            99:   wal_cksum $endian c1 c2 [string range $data $n [expr $n+7]]
        !           100:   wal_cksum $endian c1 c2 [string range $data [expr $n+24] [expr $n+24+$pgsz-1]]
        !           101: 
        !           102:   list [expr $n+16] $c1 $c2
        !           103: }
        !           104: 
        !           105: #
        !           106: # File $filename must be a WAL file on disk. Set the 'magic' field of the
        !           107: # WAL header to indicate that checksums are $endian-endian ($endian must be
        !           108: # either "big" or "little").
        !           109: #
        !           110: # Also update the wal header checksum (since the wal header contents may
        !           111: # have changed).
        !           112: #
        !           113: proc log_checksum_writemagic {filename endian} {
        !           114:   set val [expr {0x377f0682 | ($endian == "big" ? 1 : 0)}]
        !           115:   set bin [binary format I $val]
        !           116:   set fd [open $filename r+]
        !           117:   fconfigure $fd -encoding binary
        !           118:   fconfigure $fd -translation binary
        !           119:   puts -nonewline $fd $bin
        !           120: 
        !           121:   seek $fd 0
        !           122:   set blob [read $fd 24]
        !           123:   set c1 0
        !           124:   set c2 0
        !           125:   wal_cksum $endian c1 c2 $blob 
        !           126:   seek $fd 24
        !           127:   puts -nonewline $fd [binary format II $c1 $c2]
        !           128: 
        !           129:   close $fd
        !           130: }
        !           131: 
        !           132: #-------------------------------------------------------------------------
        !           133: # Test cases walcksum-1.* attempt to verify the following:
        !           134: #
        !           135: #   * That both native and non-native order checksum log files can 
        !           136: #      be recovered.
        !           137: #
        !           138: #   * That when appending to native or non-native checksum log files 
        !           139: #     SQLite continues to use the right kind of checksums.
        !           140: #
        !           141: #   * Test point 2 when the appending process is not one that recovered
        !           142: #     the log file.
        !           143: #
        !           144: #   * Test that both native and non-native checksum log files can be
        !           145: #     checkpointed. And that after doing so the next write to the log
        !           146: #     file occurs using native byte-order checksums. 
        !           147: #
        !           148: set native "big"
        !           149: if {$::tcl_platform(byteOrder) == "littleEndian"} { set native "little" }
        !           150: foreach endian {big little} {
        !           151: 
        !           152:   # Create a database. Leave some data in the log file.
        !           153:   #
        !           154:   do_test walcksum-1.$endian.1 {
        !           155:     catch { db close }
        !           156:     forcedelete test.db test.db-wal test.db-journal
        !           157:     sqlite3 db test.db
        !           158:     execsql {
        !           159:       PRAGMA page_size = 1024;
        !           160:       PRAGMA auto_vacuum = 0;
        !           161:       PRAGMA synchronous = NORMAL;
        !           162: 
        !           163:       CREATE TABLE t1(a PRIMARY KEY, b);
        !           164:       INSERT INTO t1 VALUES(1,  'one');
        !           165:       INSERT INTO t1 VALUES(2,  'two');
        !           166:       INSERT INTO t1 VALUES(3,  'three');
        !           167:       INSERT INTO t1 VALUES(5,  'five');
        !           168: 
        !           169:       PRAGMA journal_mode = WAL;
        !           170:       INSERT INTO t1 VALUES(8,  'eight');
        !           171:       INSERT INTO t1 VALUES(13, 'thirteen');
        !           172:       INSERT INTO t1 VALUES(21, 'twentyone');
        !           173:     }
        !           174: 
        !           175:     forcecopy test.db test2.db
        !           176:     forcecopy test.db-wal test2.db-wal
        !           177:     db close
        !           178: 
        !           179:     list [file size test2.db] [file size test2.db-wal]
        !           180:   } [list [expr 1024*3] [wal_file_size 6 1024]]
        !           181: 
        !           182:   # Verify that the checksums are valid for all frames and that they
        !           183:   # are calculated by interpreting data in native byte-order.
        !           184:   #
        !           185:   for {set f 1} {$f <= 6} {incr f} {
        !           186:     do_test walcksum-1.$endian.2.$f {
        !           187:       log_checksum_verify test2.db-wal $f $native
        !           188:     } 1
        !           189:   }
        !           190: 
        !           191:   # Replace all checksums in the current WAL file with $endian versions.
        !           192:   # Then check that it is still possible to recover and read the database.
        !           193:   #
        !           194:   log_checksum_writemagic test2.db-wal $endian
        !           195:   for {set f 1} {$f <= 6} {incr f} {
        !           196:     do_test walcksum-1.$endian.3.$f {
        !           197:       log_checksum_write test2.db-wal $f $endian
        !           198:       log_checksum_verify test2.db-wal $f $endian
        !           199:     } {1}
        !           200:   }
        !           201:   do_test walcksum-1.$endian.4.1 {
        !           202:     forcecopy test2.db test.db
        !           203:     forcecopy test2.db-wal test.db-wal
        !           204:     sqlite3 db test.db
        !           205:     execsql { SELECT a FROM t1 }
        !           206:   } {1 2 3 5 8 13 21}
        !           207: 
        !           208:   # Following recovery, any frames written to the log should use the same 
        !           209:   # endianness as the existing frames. Check that this is the case.
        !           210:   #
        !           211:   do_test walcksum-1.$endian.5.0 {
        !           212:     execsql { 
        !           213:       PRAGMA synchronous = NORMAL;
        !           214:       INSERT INTO t1 VALUES(34, 'thirtyfour');
        !           215:     }
        !           216:     list [file size test.db] [file size test.db-wal]
        !           217:   } [list [expr 1024*3] [wal_file_size 8 1024]]
        !           218:   for {set f 1} {$f <= 8} {incr f} {
        !           219:     do_test walcksum-1.$endian.5.$f {
        !           220:       log_checksum_verify test.db-wal $f $endian
        !           221:     } {1}
        !           222:   }
        !           223: 
        !           224:   # Now connect a second connection to the database. Check that this one
        !           225:   # (not the one that did recovery) also appends frames to the log using
        !           226:   # the same endianness for checksums as the existing frames.
        !           227:   #
        !           228:   do_test walcksum-1.$endian.6 {
        !           229:     sqlite3 db2 test.db
        !           230:     execsql { 
        !           231:       PRAGMA integrity_check;
        !           232:       SELECT a FROM t1;
        !           233:     } db2
        !           234:   } {ok 1 2 3 5 8 13 21 34}
        !           235:   do_test walcksum-1.$endian.7.0 {
        !           236:     execsql { 
        !           237:       PRAGMA synchronous = NORMAL;
        !           238:       INSERT INTO t1 VALUES(55, 'fiftyfive');
        !           239:     } db2
        !           240:     list [file size test.db] [file size test.db-wal]
        !           241:   } [list [expr 1024*3] [wal_file_size 10 1024]]
        !           242:   for {set f 1} {$f <= 10} {incr f} {
        !           243:     do_test walcksum-1.$endian.7.$f {
        !           244:       log_checksum_verify test.db-wal $f $endian
        !           245:     } {1}
        !           246:   }
        !           247: 
        !           248:   # Now that both the recoverer and non-recoverer have added frames to the
        !           249:   # log file, check that it can still be recovered.
        !           250:   #
        !           251:   forcecopy test.db test2.db
        !           252:   forcecopy test.db-wal test2.db-wal
        !           253:   do_test walcksum-1.$endian.7.11 {
        !           254:     sqlite3 db3 test2.db
        !           255:     execsql { 
        !           256:       PRAGMA integrity_check;
        !           257:       SELECT a FROM t1;
        !           258:     } db3
        !           259:   } {ok 1 2 3 5 8 13 21 34 55}
        !           260:   db3 close
        !           261: 
        !           262:   # Run a checkpoint on the database file. Then, check that any frames written
        !           263:   # to the start of the log use native byte-order checksums.
        !           264:   #
        !           265:   do_test walcksum-1.$endian.8.1 {
        !           266:     execsql {
        !           267:       PRAGMA wal_checkpoint;
        !           268:       INSERT INTO t1 VALUES(89, 'eightynine');
        !           269:     }
        !           270:     log_checksum_verify test.db-wal 1 $native
        !           271:   } {1}
        !           272:   do_test walcksum-1.$endian.8.2 {
        !           273:     log_checksum_verify test.db-wal 2 $native
        !           274:   } {1}
        !           275:   do_test walcksum-1.$endian.8.3 {
        !           276:     log_checksum_verify test.db-wal 3 $native
        !           277:   } {0}
        !           278: 
        !           279:   do_test walcksum-1.$endian.9 {
        !           280:     execsql { 
        !           281:       PRAGMA integrity_check;
        !           282:       SELECT a FROM t1;
        !           283:     } db2
        !           284:   } {ok 1 2 3 5 8 13 21 34 55 89}
        !           285: 
        !           286:   catch { db close }
        !           287:   catch { db2 close }
        !           288: }
        !           289: 
        !           290: #-------------------------------------------------------------------------
        !           291: # Test case walcksum-2.* tests that if a statement transaction is rolled
        !           292: # back after frames are written to the WAL, and then (after writing some
        !           293: # more) the outer transaction is committed, the WAL file is still correctly
        !           294: # formatted (and can be recovered by a second process if required).
        !           295: #
        !           296: do_test walcksum-2.1 {
        !           297:   forcedelete test.db test.db-wal test.db-journal
        !           298:   sqlite3 db test.db
        !           299:   execsql {
        !           300:     PRAGMA synchronous = NORMAL;
        !           301:     PRAGMA page_size = 1024;
        !           302:     PRAGMA journal_mode = WAL;
        !           303:     PRAGMA cache_size = 10;
        !           304:     CREATE TABLE t1(x PRIMARY KEY);
        !           305:     PRAGMA wal_checkpoint;
        !           306:     INSERT INTO t1 VALUES(randomblob(800));
        !           307:     BEGIN;
        !           308:       INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*   2 */
        !           309:       INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*   4 */
        !           310:       INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*   8 */
        !           311:       INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  16 */
        !           312:       SAVEPOINT one;
        !           313:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  32 */
        !           314:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  64 */
        !           315:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   /* 128 */
        !           316:         INSERT INTO t1 SELECT randomblob(800) FROM t1;   /* 256 */
        !           317:       ROLLBACK TO one;
        !           318:       INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  32 */
        !           319:       INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  64 */
        !           320:       INSERT INTO t1 SELECT randomblob(800) FROM t1;   /* 128 */
        !           321:       INSERT INTO t1 SELECT randomblob(800) FROM t1;   /* 256 */
        !           322:     COMMIT;
        !           323:   }
        !           324: 
        !           325:   forcecopy test.db test2.db
        !           326:   forcecopy test.db-wal test2.db-wal
        !           327: 
        !           328:   sqlite3 db2 test2.db
        !           329:   execsql {
        !           330:     PRAGMA integrity_check;
        !           331:     SELECT count(*) FROM t1;
        !           332:   } db2
        !           333: } {ok 256}
        !           334: catch { db close }
        !           335: catch { db2 close }
        !           336: 
        !           337: #-------------------------------------------------------------------------
        !           338: # Test case walcksum-3.* tests that the checksum calculation detects single 
        !           339: # byte changes to frame or frame-header data and considers the frame
        !           340: # invalid as a result.
        !           341: #
        !           342: do_test walcksum-3.1 {
        !           343:   forcedelete test.db test.db-wal test.db-journal
        !           344:   sqlite3 db test.db
        !           345: 
        !           346:   execsql {
        !           347:     PRAGMA synchronous = NORMAL;
        !           348:     PRAGMA page_size = 1024;
        !           349:     CREATE TABLE t1(a, b);
        !           350:     INSERT INTO t1 VALUES(1, randomblob(300));
        !           351:     INSERT INTO t1 VALUES(2, randomblob(300));
        !           352:     PRAGMA journal_mode = WAL;
        !           353:     INSERT INTO t1 VALUES(3, randomblob(300));
        !           354:   }
        !           355: 
        !           356:   file size test.db-wal
        !           357: } [wal_file_size 1 1024]
        !           358: do_test walcksum-3.2 {
        !           359:   forcecopy test.db-wal test2.db-wal
        !           360:   forcecopy test.db test2.db
        !           361:   sqlite3 db2 test2.db
        !           362:   execsql { SELECT a FROM t1 } db2
        !           363: } {1 2 3}
        !           364: db2 close
        !           365: forcecopy test.db test2.db
        !           366: 
        !           367: 
        !           368: foreach incr {1 2 3 20 40 60 80 100 120 140 160 180 200 220 240 253 254 255} {
        !           369:   do_test walcksum-3.3.$incr {
        !           370:     set FAIL 0
        !           371:     for {set iOff 0} {$iOff < [wal_file_size 1 1024]} {incr iOff} {
        !           372: 
        !           373:       forcecopy test.db-wal test2.db-wal
        !           374:       set fd [open test2.db-wal r+]
        !           375:       fconfigure $fd -encoding binary
        !           376:       fconfigure $fd -translation binary
        !           377:   
        !           378:       seek $fd $iOff
        !           379:       binary scan [read $fd 1] c x
        !           380:       seek $fd $iOff
        !           381:       puts -nonewline $fd [binary format c [expr {($x+$incr)&0xFF}]]
        !           382:       close $fd
        !           383:     
        !           384:       sqlite3 db2 test2.db
        !           385:       if { [execsql { SELECT a FROM t1 } db2] != "1 2" } {set FAIL 1}
        !           386:       db2 close
        !           387:     }
        !           388:     set FAIL
        !           389:   } {0}
        !           390: }
        !           391:   
        !           392: finish_test
        !           393: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>