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>