gitlab.com/CoiaPrant/sqlite3@v1.19.1/testdata/overlay/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  
    26  # This test relies on a forcedelete of an open file
    27  # resulting in: error deleting "test.db": permission denied
    28  # Not possible to remove the open file
    29  if {$::tcl_platform(platform)!="windows"} {
    30  
    31  do_faultsim_test 1.0 -prep {
    32    faultsim_delete_and_reopen
    33    sqlite3 db2 test.db
    34    db2 eval { 
    35      CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
    36      INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
    37      INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
    38      PRAGMA journal_mode = wal;
    39      INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
    40      BEGIN;
    41        SELECT a FROM t1;
    42    }
    43    set ::snapshot [sqlite3_snapshot_get db2 main] 
    44    db2 eval COMMIT
    45    db2 eval {
    46      UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
    47      INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
    48      INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
    49      INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
    50    }
    51  } -body {
    52    db eval { PRAGMA wal_checkpoint }
    53  } -test {
    54    db2 eval BEGIN
    55    if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
    56      if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
    57        error "error is $msg" 
    58      }
    59    } else {
    60      set res [db2 eval { 
    61        SELECT a FROM t1;
    62        PRAGMA integrity_check;
    63      }]
    64      if {$res != "1 2 3 ok"} { error "res is $res" }
    65    }
    66  
    67    sqlite3_snapshot_free $::snapshot
    68  }
    69  }
    70  
    71  #-------------------------------------------------------------------------
    72  # This test is similar to the previous one. Except, after the 
    73  # "PRAGMA wal_checkpoint" command fails the db is closed and reopened
    74  # so as to require wal file recovery. It should not be possible to open
    75  # a snapshot that is part of the body of a recovered wal file.
    76  #
    77  do_faultsim_test 2.0 -prep {
    78    faultsim_delete_and_reopen
    79    db eval { 
    80      CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
    81      INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
    82      INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
    83      PRAGMA journal_mode = wal;
    84      INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
    85      BEGIN;
    86        SELECT a FROM t1;
    87    }
    88    set ::snapshot [sqlite3_snapshot_get db main] 
    89    db eval COMMIT
    90  
    91    db eval {
    92      UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
    93      INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
    94      INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
    95      INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
    96    }
    97  } -body {
    98    db eval { PRAGMA wal_checkpoint }
    99  } -test {
   100  
   101    db_save
   102    db close
   103    db_restore_and_reopen
   104    db eval { SELECT * FROM t1 }
   105    
   106    db eval BEGIN
   107    if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
   108      if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
   109        error "error is $msg" 
   110      }
   111    } else {
   112      # This branch should actually never be taken. But it was useful in
   113      # determining whether or not this test was actually working (by 
   114      # running a modified version of SQLite that allowed snapshots to be
   115      # opened following a recovery).
   116      error "TEST HAS FAILED"
   117  
   118      set res [db eval { 
   119        SELECT a FROM t1;
   120        PRAGMA integrity_check;
   121      }]
   122      if {$res != "1 2 3 ok"} { error "res is $res" }
   123    }
   124  
   125    sqlite3_snapshot_free $::snapshot
   126  }
   127  
   128  #-------------------------------------------------------------------------
   129  # Test the handling of faults that occur within sqlite3_snapshot_open().
   130  #
   131  do_faultsim_test 3.0 -prep {
   132    faultsim_delete_and_reopen
   133    db eval { 
   134      CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
   135      INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
   136      INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
   137      PRAGMA journal_mode = wal;
   138      INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
   139      BEGIN;
   140        SELECT a FROM t1;
   141    }
   142    set ::snapshot [sqlite3_snapshot_get db main] 
   143    db eval COMMIT
   144    db eval {
   145      UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
   146      INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
   147      INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
   148      INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
   149      BEGIN;
   150    }
   151  } -body {
   152    if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } {
   153      error $msg
   154    }
   155  } -test {
   156    faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM} \
   157                                {1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ}
   158    if {$testrc==0} {
   159      set res [db eval { 
   160        SELECT a FROM t1;
   161        PRAGMA integrity_check;
   162      }]
   163      if {$res != "1 2 3 ok"} { error "res is $res" }
   164    }
   165  
   166    sqlite3_snapshot_free $::snapshot
   167  }
   168  
   169  #-------------------------------------------------------------------------
   170  # Test the handling of faults that occur within sqlite3_snapshot_recover().
   171  #
   172  reset_db
   173  do_execsql_test 4.0 {
   174    PRAGMA journal_mode = wal;
   175    CREATE TABLE t1(zzz);
   176    INSERT INTO t1 VALUES('abc');
   177    INSERT INTO t1 VALUES('def');
   178  } {wal}
   179  faultsim_save_and_close
   180  
   181  do_test 4.0.1 {
   182    faultsim_restore_and_reopen
   183    db eval { SELECT * FROM sqlite_master } 
   184    sqlite3_snapshot_recover db main
   185  } {}
   186  db close
   187  
   188  do_faultsim_test 4.0 -faults oom* -prep {
   189    faultsim_restore_and_reopen
   190    db eval { SELECT * FROM sqlite_master } 
   191  } -body {
   192    sqlite3_snapshot_recover db main
   193  } -test {
   194    faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
   195  }
   196  
   197  # The following test cases contrive to call sqlite3_snapshot_recover()
   198  # before all pages of the *-shm file have been mapped. This tests an
   199  # extra branch of error handling logic in snapshot_recover().
   200  #
   201  reset_db
   202  do_execsql_test 4.1.0 {
   203    PRAGMA page_size = 512;
   204    PRAGMA journal_mode = wal;
   205    PRAGMA wal_autocheckpoint = 0;
   206    CREATE TABLE t1(zzz);
   207    INSERT INTO t1 VALUES(randomblob( 500 * 9500 ));
   208    PRAGMA user_version = 211;
   209  } {wal 0}
   210  
   211  do_test 4.1.1 {
   212    list [file size test.db-shm] [file size test.db]
   213  } {98304 512}
   214  
   215  faultsim_save_and_close
   216  do_faultsim_test 4.1 -faults shm* -prep {
   217    catch { db2 close } 
   218    catch { db close } 
   219    faultsim_restore_and_reopen
   220    sqlite3 db2 test.db
   221    db2 eval { SELECT * FROM sqlite_master } 
   222    db eval BEGIN
   223    sqlite3_snapshot_get_blob db main
   224    db eval COMMIT
   225  } -body {
   226    sqlite3_snapshot_recover db main
   227  } -test {
   228    faultsim_test_result {0 {}} {1 SQLITE_IOERR}
   229  }
   230  
   231  #-------------------------------------------------------------------------
   232  # Test the handling of faults that occur within sqlite3_snapshot_get().
   233  #
   234  reset_db
   235  do_execsql_test 5.0 {
   236    PRAGMA page_size = 512;
   237    PRAGMA journal_mode = wal;
   238    PRAGMA wal_autocheckpoint = 0;
   239    CREATE TABLE t1(zzz);
   240    INSERT INTO t1 VALUES(randomblob( 5000 ));
   241    PRAGMA user_version = 211;
   242  } {wal 0}
   243  faultsim_save_and_close
   244  
   245  do_faultsim_test 5 -prep {
   246    faultsim_restore_and_reopen
   247    execsql { SELECT count(*) FROM sqlite_master }
   248    execsql BEGIN
   249  } -body {
   250    sqlite3_snapshot_get_blob db main
   251    set {} {}
   252  } -test {
   253    execsql END
   254    faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM}
   255  }
   256  
   257  
   258  finish_test