| OLD | NEW | 
 | (Empty) | 
|    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   file delete -force 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 occured 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 { file delete -force test.db } |  | 
|  159   catch { file delete -force 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 occured 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 |  | 
| OLD | NEW |