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

     1  # 2009 January 30
     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
    12  # focus of this file is testing the handling of IO errors by the
    13  # sqlite3_backup_XXX APIs.
    14  #
    15  # $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $
    16  
    17  set testdir [file dirname $argv0]
    18  source $testdir/tester.tcl
    19  
    20  proc data_checksum {db file} { 
    21    $db one "SELECT md5sum(a, b) FROM ${file}.t1" 
    22  }
    23  proc test_contents {name db1 file1 db2 file2} {
    24    $db2 eval {select * from sqlite_master}
    25    $db1 eval {select * from sqlite_master}
    26    set checksum [data_checksum $db2 $file2]
    27    uplevel [list do_test $name [list data_checksum $db1 $file1] $checksum]
    28  }
    29  
    30  #--------------------------------------------------------------------
    31  # This proc creates a database of approximately 290 pages. Depending
    32  # on whether or not auto-vacuum is configured. Test cases backup_ioerr-1.*
    33  # verify nothing more than this assumption.
    34  #
    35  proc populate_database {db {xtra_large 0}} {
    36    execsql {
    37      BEGIN;
    38      CREATE TABLE t1(a, b);
    39      INSERT INTO t1 VALUES(1, randstr(1000,1000));
    40      INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1;
    41      INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1;
    42      INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1;
    43      INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1;
    44      INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1;
    45      INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1;
    46      CREATE INDEX i1 ON t1(b);
    47      COMMIT;
    48    } $db
    49    if {$xtra_large} {
    50      execsql { INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1 } $db
    51    }
    52  }
    53  do_test backup_ioerr-1.1 {
    54    populate_database db
    55    set nPage [expr {[file size test.db] / 1024}]
    56    expr {$nPage>130 && $nPage<160}
    57  } {1}
    58  do_test backup_ioerr-1.2 {
    59    expr {[file size test.db] > $sqlite_pending_byte}
    60  } {1}
    61  do_test backup_ioerr-1.3 {
    62    db close
    63    forcedelete test.db
    64  } {}
    65  
    66  # Turn off IO error simulation.
    67  #
    68  proc clear_ioerr_simulation {} {
    69    set ::sqlite_io_error_hit 0
    70    set ::sqlite_io_error_hardhit 0
    71    set ::sqlite_io_error_pending 0
    72    set ::sqlite_io_error_persist 0
    73  }
    74  
    75  #--------------------------------------------------------------------
    76  # The following procedure runs with SQLite's IO error simulation 
    77  # enabled.
    78  #
    79  #   1) Start with a reasonably sized database. One that includes the
    80  #      pending-byte (locking) page.
    81  #
    82  #   2) Open a backup process. Set the cache-size for the destination
    83  #      database to 10 pages only.
    84  #
    85  #   3) Step the backup process N times to partially backup the database
    86  #      file. If an IO error is reported, then the backup process is
    87  #      concluded with a call to backup_finish().
    88  #
    89  #      If an IO error occurs, verify that:
    90  #
    91  #      * the call to backup_step() returns an SQLITE_IOERR_XXX error code.
    92  #
    93  #      * after the failed call to backup_step() but before the call to
    94  #        backup_finish() the destination database handle error code and 
    95  #        error message remain unchanged.
    96  #
    97  #      * the call to backup_finish() returns an SQLITE_IOERR_XXX error code.
    98  #
    99  #      * following the call to backup_finish(), the destination database
   100  #        handle has been populated with an error code and error message.
   101  #
   102  #   4) Write to the database via the source database connection. Check 
   103  #      that:
   104  #
   105  #      * If an IO error occurs while writing the source database, the
   106  #        write operation should report an IO error. The backup should 
   107  #        proceed as normal.
   108  #
   109  #      * If an IO error occurs while updating the backup, the write 
   110  #        operation should proceed normally. The error should be reported
   111  #        from the next call to backup_step() (in step 5 of this test
   112  #        procedure).
   113  #
   114  #   5) Step the backup process to finish the backup. If an IO error is 
   115  #      reported, then the backup process is concluded with a call to 
   116  #      backup_finish().
   117  #
   118  #      Test that if an IO error occurs, or if one occurred while updating
   119  #      the backup database during step 4, then the conditions listed
   120  #      under step 3 are all true.
   121  #
   122  #   6) Finish the backup process.
   123  #
   124  #   * If the backup succeeds (backup_finish() returns SQLITE_OK), then
   125  #     the contents of the backup database should match that of the
   126  #     source database.
   127  #
   128  #   * If the backup fails (backup_finish() returns other than SQLITE_OK), 
   129  #     then the contents of the backup database should be as they were 
   130  #     before the operation was started.
   131  #
   132  # The following factors are varied:
   133  #
   134  #   * Destination database is initially larger than the source database, OR
   135  #   * Destination database is initially smaller than the source database.
   136  #
   137  #   * IO errors are transient, OR
   138  #   * IO errors are persistent.
   139  #
   140  #   * Destination page-size is smaller than the source.
   141  #   * Destination page-size is the same as the source.
   142  #   * Destination page-size is larger than the source.
   143  #
   144  
   145  set iTest 1
   146  foreach bPersist {0 1} {
   147  foreach iDestPagesize {512 1024 4096} {
   148  foreach zSetupBak [list "" {populate_database ddb 1}] {
   149  
   150    incr iTest
   151    set bStop 0
   152  for {set iError 1} {$bStop == 0} {incr iError} {
   153    # Disable IO error simulation.
   154    clear_ioerr_simulation
   155  
   156    catch { ddb close }
   157    catch { sdb close }
   158    catch { forcedelete test.db }
   159    catch { forcedelete bak.db }
   160  
   161    # Open the source and destination databases.
   162    sqlite3 sdb test.db
   163    sqlite3 ddb bak.db
   164  
   165    # Step 1: Populate the source and destination databases.
   166    populate_database sdb
   167    ddb eval "PRAGMA page_size = $iDestPagesize"
   168    ddb eval "PRAGMA cache_size = 10"
   169    eval $zSetupBak
   170  
   171    # Step 2: Open the backup process.
   172    sqlite3_backup B ddb main sdb main
   173  
   174    # Enable IO error simulation.
   175    set ::sqlite_io_error_pending $iError
   176    set ::sqlite_io_error_persist $bPersist
   177  
   178    # Step 3: Partially backup the database. If an IO error occurs, check
   179    # a few things then skip to the next iteration of the loop.
   180    #
   181    set rc [B step 100]
   182    if {$::sqlite_io_error_hardhit} {
   183  
   184      do_test backup_ioerr-$iTest.$iError.1 {
   185        string match SQLITE_IOERR* $rc
   186      } {1}
   187      do_test backup_ioerr-$iTest.$iError.2 {
   188        list [sqlite3_errcode ddb] [sqlite3_errmsg ddb]
   189      } {SQLITE_OK {not an error}}
   190  
   191      set rc [B finish]
   192      do_test backup_ioerr-$iTest.$iError.3 {
   193        string match SQLITE_IOERR* $rc
   194      } {1}
   195  
   196      do_test backup_ioerr-$iTest.$iError.4 {
   197        sqlite3_errmsg ddb
   198      } {disk I/O error}
   199  
   200      clear_ioerr_simulation
   201      sqlite3 ddb bak.db
   202      integrity_check backup_ioerr-$iTest.$iError.5 ddb
   203  
   204      continue
   205    }
   206  
   207    # No IO error was encountered during step 3. Check that backup_step()
   208    # returned SQLITE_OK before proceding.
   209    do_test backup_ioerr-$iTest.$iError.6 {
   210      expr {$rc eq "SQLITE_OK"}
   211    } {1}
   212  
   213    # Step 4: Write to the source database.
   214    set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb]
   215  
   216    if {[lindex $rc 0] && $::sqlite_io_error_persist==0} {
   217      # The IO error occurred while updating the source database. In this
   218      # case the backup should be able to continue.
   219      set rc [B step 5000]
   220      if { $rc != "SQLITE_IOERR_UNLOCK" } {
   221        do_test backup_ioerr-$iTest.$iError.7 {
   222          list [B step 5000] [B finish]
   223        } {SQLITE_DONE SQLITE_OK}
   224  
   225        clear_ioerr_simulation
   226        test_contents backup_ioerr-$iTest.$iError.8 ddb main sdb main
   227        integrity_check backup_ioerr-$iTest.$iError.9 ddb
   228      } else {
   229        do_test backup_ioerr-$iTest.$iError.10 {
   230          B finish
   231        } {SQLITE_IOERR_UNLOCK}
   232      }
   233  
   234      clear_ioerr_simulation
   235      sqlite3 ddb bak.db
   236      integrity_check backup_ioerr-$iTest.$iError.11 ddb
   237  
   238      continue
   239    }
   240  
   241    # Step 5: Finish the backup operation. If an IO error occurs, check that
   242    # it is reported correctly and skip to the next iteration of the loop.
   243    #
   244    set rc [B step 5000]
   245    if {$rc != "SQLITE_DONE"} {
   246      do_test backup_ioerr-$iTest.$iError.12 {
   247        string match SQLITE_IOERR* $rc
   248      } {1}
   249      do_test backup_ioerr-$iTest.$iError.13 {
   250        list [sqlite3_errcode ddb] [sqlite3_errmsg ddb]
   251      } {SQLITE_OK {not an error}}
   252  
   253      set rc [B finish]
   254      do_test backup_ioerr-$iTest.$iError.14 {
   255        string match SQLITE_IOERR* $rc
   256      } {1}
   257      do_test backup_ioerr-$iTest.$iError.15 {
   258        sqlite3_errmsg ddb
   259      } {disk I/O error}
   260  
   261      clear_ioerr_simulation
   262      sqlite3 ddb bak.db
   263      integrity_check backup_ioerr-$iTest.$iError.16 ddb
   264  
   265      continue
   266    }
   267  
   268    # The backup was successfully completed.
   269    #
   270    do_test backup_ioerr-$iTest.$iError.17 {
   271      list [set rc] [B finish]
   272    } {SQLITE_DONE SQLITE_OK}
   273  
   274    clear_ioerr_simulation
   275    sqlite3 sdb test.db
   276    sqlite3 ddb bak.db
   277  
   278    test_contents backup_ioerr-$iTest.$iError.18 ddb main sdb main
   279    integrity_check backup_ioerr-$iTest.$iError.19 ddb
   280  
   281    set bStop [expr $::sqlite_io_error_pending<=0]
   282  }}}}
   283  
   284  catch { sdb close }
   285  catch { ddb close }
   286  finish_test