modernc.org/cc@v1.0.1/v2/testdata/_sqlite/test/snapshot_fault.test (about)

     1  # 2015 December 10
     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. The focus
    12  # of this file is the sqlite3_snapshot_xxx() APIs.
    13  #
    14  
    15  set testdir [file dirname $argv0]
    16  source $testdir/tester.tcl
    17  ifcapable !snapshot {finish_test; return}
    18  set testprefix snapshot_fault
    19  
    20  #-------------------------------------------------------------------------
    21  # Check that an sqlite3_snapshot_open() client cannot be tricked into
    22  # reading a corrupt snapshot even if a second client fails while 
    23  # checkpointing the db.
    24  #
    25  do_faultsim_test 1.0 -prep {
    26    faultsim_delete_and_reopen
    27    sqlite3 db2 test.db
    28    db2 eval { 
    29      CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
    30      INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
    31      INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
    32      PRAGMA journal_mode = wal;
    33      INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
    34      BEGIN;
    35        SELECT a FROM t1;
    36    }
    37    set ::snapshot [sqlite3_snapshot_get db2 main] 
    38    db2 eval COMMIT
    39    db2 eval {
    40      UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
    41      INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
    42      INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
    43      INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
    44    }
    45  } -body {
    46    db eval { PRAGMA wal_checkpoint }
    47  } -test {
    48    db2 eval BEGIN
    49    if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
    50      if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
    51        error "error is $msg" 
    52      }
    53    } else {
    54      set res [db2 eval { 
    55        SELECT a FROM t1;
    56        PRAGMA integrity_check;
    57      }]
    58      if {$res != "1 2 3 ok"} { error "res is $res" }
    59    }
    60  
    61    sqlite3_snapshot_free $::snapshot
    62  }
    63  
    64  #-------------------------------------------------------------------------
    65  # This test is similar to the previous one. Except, after the 
    66  # "PRAGMA wal_checkpoint" command fails the db is closed and reopened
    67  # so as to require wal file recovery. It should not be possible to open
    68  # a snapshot that is part of the body of a recovered wal file.
    69  #
    70  do_faultsim_test 2.0 -prep {
    71    faultsim_delete_and_reopen
    72    db eval { 
    73      CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
    74      INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
    75      INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
    76      PRAGMA journal_mode = wal;
    77      INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
    78      BEGIN;
    79        SELECT a FROM t1;
    80    }
    81    set ::snapshot [sqlite3_snapshot_get db main] 
    82    db eval COMMIT
    83  
    84    db eval {
    85      UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
    86      INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
    87      INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
    88      INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
    89    }
    90  } -body {
    91    db eval { PRAGMA wal_checkpoint }
    92  } -test {
    93  
    94    db_save
    95    db close
    96    db_restore_and_reopen
    97    db eval { SELECT * FROM t1 }
    98    
    99    db eval BEGIN
   100    if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
   101      if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
   102        error "error is $msg" 
   103      }
   104    } else {
   105      # This branch should actually never be taken. But it was useful in
   106      # determining whether or not this test was actually working (by 
   107      # running a modified version of SQLite that allowed snapshots to be
   108      # opened following a recovery).
   109      error "TEST HAS FAILED"
   110  
   111      set res [db eval { 
   112        SELECT a FROM t1;
   113        PRAGMA integrity_check;
   114      }]
   115      if {$res != "1 2 3 ok"} { error "res is $res" }
   116    }
   117  
   118    sqlite3_snapshot_free $::snapshot
   119  }
   120  
   121  #-------------------------------------------------------------------------
   122  # Test the handling of faults that occur within sqlite3_snapshot_open().
   123  #
   124  do_faultsim_test 3.0 -prep {
   125    faultsim_delete_and_reopen
   126    db eval { 
   127      CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
   128      INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
   129      INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
   130      PRAGMA journal_mode = wal;
   131      INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
   132      BEGIN;
   133        SELECT a FROM t1;
   134    }
   135    set ::snapshot [sqlite3_snapshot_get db main] 
   136    db eval COMMIT
   137    db eval {
   138      UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
   139      INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
   140      INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
   141      INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
   142      BEGIN;
   143    }
   144  } -body {
   145    if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } {
   146      error $msg
   147    }
   148  } -test {
   149    faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM} \
   150                                {1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ}
   151    if {$testrc==0} {
   152      set res [db eval { 
   153        SELECT a FROM t1;
   154        PRAGMA integrity_check;
   155      }]
   156      if {$res != "1 2 3 ok"} { error "res is $res" }
   157    }
   158  
   159    sqlite3_snapshot_free $::snapshot
   160  }
   161  
   162  #-------------------------------------------------------------------------
   163  # Test the handling of faults that occur within sqlite3_snapshot_recover().
   164  #
   165  reset_db
   166  do_execsql_test 4.0 {
   167    PRAGMA journal_mode = wal;
   168    CREATE TABLE t1(zzz);
   169    INSERT INTO t1 VALUES('abc');
   170    INSERT INTO t1 VALUES('def');
   171  } {wal}
   172  faultsim_save_and_close
   173  
   174  do_test 4.0.1 {
   175    faultsim_restore_and_reopen
   176    db eval { SELECT * FROM sqlite_master } 
   177    sqlite3_snapshot_recover db main
   178  } {}
   179  db close
   180  
   181  do_faultsim_test 4.0 -faults oom* -prep {
   182    faultsim_restore_and_reopen
   183    db eval { SELECT * FROM sqlite_master } 
   184  } -body {
   185    sqlite3_snapshot_recover db main
   186  } -test {
   187    faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
   188  }
   189  
   190  # The following test cases contrive to call sqlite3_snapshot_recover()
   191  # before all pages of the *-shm file have been mapped. This tests an
   192  # extra branch of error handling logic in snapshot_recover().
   193  #
   194  reset_db
   195  do_execsql_test 4.1.0 {
   196    PRAGMA page_size = 512;
   197    PRAGMA journal_mode = wal;
   198    PRAGMA wal_autocheckpoint = 0;
   199    CREATE TABLE t1(zzz);
   200    INSERT INTO t1 VALUES(randomblob( 500 * 9500 ));
   201    PRAGMA user_version = 211;
   202  } {wal 0}
   203  
   204  do_test 4.1.1 {
   205    list [file size test.db-shm] [file size test.db]
   206  } {98304 512}
   207  
   208  faultsim_save_and_close
   209  do_faultsim_test 4.1 -faults shm* -prep {
   210    catch { db2 close } 
   211    catch { db close } 
   212    faultsim_restore_and_reopen
   213    sqlite3 db2 test.db
   214    db2 eval { SELECT * FROM sqlite_master } 
   215    db eval BEGIN
   216    sqlite3_snapshot_get_blob db main
   217    db eval COMMIT
   218  } -body {
   219    sqlite3_snapshot_recover db main
   220  } -test {
   221    faultsim_test_result {0 {}} {1 SQLITE_IOERR}
   222  }
   223  
   224  
   225  
   226  finish_test