gitlab.com/CoiaPrant/sqlite3@v1.19.1/testdata/tcl/walcksum.test (about)

     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  finish_test