xref: /sqlite-3.40.0/test/backup_ioerr.test (revision 48864df9)
1dc2c4915Sdrh# 2009 January 30
20410302eSdanielk1977#
30410302eSdanielk1977# The author disclaims copyright to this source code.  In place of
40410302eSdanielk1977# a legal notice, here is a blessing:
50410302eSdanielk1977#
60410302eSdanielk1977#    May you do good and not evil.
70410302eSdanielk1977#    May you find forgiveness for yourself and forgive others.
80410302eSdanielk1977#    May you share freely, never taking more than you give.
90410302eSdanielk1977#
100410302eSdanielk1977#***********************************************************************
110410302eSdanielk1977# This file implements regression tests for SQLite library.  The
120410302eSdanielk1977# focus of this file is testing the handling of IO errors by the
130410302eSdanielk1977# sqlite3_backup_XXX APIs.
140410302eSdanielk1977#
1507a10893Sdanielk1977# $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $
160410302eSdanielk1977
170410302eSdanielk1977set testdir [file dirname $argv0]
180410302eSdanielk1977source $testdir/tester.tcl
190410302eSdanielk1977
200410302eSdanielk1977proc data_checksum {db file} {
210410302eSdanielk1977  $db one "SELECT md5sum(a, b) FROM ${file}.t1"
220410302eSdanielk1977}
230410302eSdanielk1977proc test_contents {name db1 file1 db2 file2} {
240410302eSdanielk1977  $db2 eval {select * from sqlite_master}
250410302eSdanielk1977  $db1 eval {select * from sqlite_master}
260410302eSdanielk1977  set checksum [data_checksum $db2 $file2]
270410302eSdanielk1977  uplevel [list do_test $name [list data_checksum $db1 $file1] $checksum]
280410302eSdanielk1977}
290410302eSdanielk1977
300410302eSdanielk1977#--------------------------------------------------------------------
310410302eSdanielk1977# This proc creates a database of approximately 290 pages. Depending
320410302eSdanielk1977# on whether or not auto-vacuum is configured. Test cases backup_ioerr-1.*
330410302eSdanielk1977# verify nothing more than this assumption.
340410302eSdanielk1977#
350410302eSdanielk1977proc populate_database {db {xtra_large 0}} {
360410302eSdanielk1977  execsql {
370410302eSdanielk1977    BEGIN;
380410302eSdanielk1977    CREATE TABLE t1(a, b);
390410302eSdanielk1977    INSERT INTO t1 VALUES(1, randstr(1000,1000));
400410302eSdanielk1977    INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1;
410410302eSdanielk1977    INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1;
420410302eSdanielk1977    INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1;
430410302eSdanielk1977    INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1;
440410302eSdanielk1977    INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1;
450410302eSdanielk1977    INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1;
460410302eSdanielk1977    CREATE INDEX i1 ON t1(b);
470410302eSdanielk1977    COMMIT;
480410302eSdanielk1977  } $db
490410302eSdanielk1977  if {$xtra_large} {
500410302eSdanielk1977    execsql { INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1 } $db
510410302eSdanielk1977  }
520410302eSdanielk1977}
530410302eSdanielk1977do_test backup_ioerr-1.1 {
540410302eSdanielk1977  populate_database db
550410302eSdanielk1977  set nPage [expr {[file size test.db] / 1024}]
5607a10893Sdanielk1977  expr {$nPage>130 && $nPage<160}
570410302eSdanielk1977} {1}
580410302eSdanielk1977do_test backup_ioerr-1.2 {
590410302eSdanielk1977  expr {[file size test.db] > $sqlite_pending_byte}
600410302eSdanielk1977} {1}
610410302eSdanielk1977do_test backup_ioerr-1.3 {
620410302eSdanielk1977  db close
63fda06befSmistachkin  forcedelete test.db
640410302eSdanielk1977} {}
650410302eSdanielk1977
660410302eSdanielk1977# Turn off IO error simulation.
670410302eSdanielk1977#
680410302eSdanielk1977proc clear_ioerr_simulation {} {
690410302eSdanielk1977  set ::sqlite_io_error_hit 0
700410302eSdanielk1977  set ::sqlite_io_error_hardhit 0
710410302eSdanielk1977  set ::sqlite_io_error_pending 0
720410302eSdanielk1977  set ::sqlite_io_error_persist 0
730410302eSdanielk1977}
740410302eSdanielk1977
750410302eSdanielk1977#--------------------------------------------------------------------
760410302eSdanielk1977# The following procedure runs with SQLite's IO error simulation
770410302eSdanielk1977# enabled.
780410302eSdanielk1977#
790410302eSdanielk1977#   1) Start with a reasonably sized database. One that includes the
800410302eSdanielk1977#      pending-byte (locking) page.
810410302eSdanielk1977#
820410302eSdanielk1977#   2) Open a backup process. Set the cache-size for the destination
830410302eSdanielk1977#      database to 10 pages only.
840410302eSdanielk1977#
850410302eSdanielk1977#   3) Step the backup process N times to partially backup the database
860410302eSdanielk1977#      file. If an IO error is reported, then the backup process is
870410302eSdanielk1977#      concluded with a call to backup_finish().
880410302eSdanielk1977#
890410302eSdanielk1977#      If an IO error occurs, verify that:
900410302eSdanielk1977#
910410302eSdanielk1977#      * the call to backup_step() returns an SQLITE_IOERR_XXX error code.
920410302eSdanielk1977#
930410302eSdanielk1977#      * after the failed call to backup_step() but before the call to
940410302eSdanielk1977#        backup_finish() the destination database handle error code and
950410302eSdanielk1977#        error message remain unchanged.
960410302eSdanielk1977#
970410302eSdanielk1977#      * the call to backup_finish() returns an SQLITE_IOERR_XXX error code.
980410302eSdanielk1977#
990410302eSdanielk1977#      * following the call to backup_finish(), the destination database
1000410302eSdanielk1977#        handle has been populated with an error code and error message.
1010410302eSdanielk1977#
1020410302eSdanielk1977#   4) Write to the database via the source database connection. Check
1030410302eSdanielk1977#      that:
1040410302eSdanielk1977#
1050410302eSdanielk1977#      * If an IO error occurs while writing the source database, the
1060410302eSdanielk1977#        write operation should report an IO error. The backup should
1070410302eSdanielk1977#        proceed as normal.
1080410302eSdanielk1977#
1090410302eSdanielk1977#      * If an IO error occurs while updating the backup, the write
1100410302eSdanielk1977#        operation should proceed normally. The error should be reported
1110410302eSdanielk1977#        from the next call to backup_step() (in step 5 of this test
1120410302eSdanielk1977#        procedure).
1130410302eSdanielk1977#
1140410302eSdanielk1977#   5) Step the backup process to finish the backup. If an IO error is
1150410302eSdanielk1977#      reported, then the backup process is concluded with a call to
1160410302eSdanielk1977#      backup_finish().
1170410302eSdanielk1977#
118*48864df9Smistachkin#      Test that if an IO error occurs, or if one occurred while updating
1190410302eSdanielk1977#      the backup database during step 4, then the conditions listed
1200410302eSdanielk1977#      under step 3 are all true.
1210410302eSdanielk1977#
1220410302eSdanielk1977#   6) Finish the backup process.
1230410302eSdanielk1977#
1240410302eSdanielk1977#   * If the backup succeeds (backup_finish() returns SQLITE_OK), then
1250410302eSdanielk1977#     the contents of the backup database should match that of the
1260410302eSdanielk1977#     source database.
1270410302eSdanielk1977#
1280410302eSdanielk1977#   * If the backup fails (backup_finish() returns other than SQLITE_OK),
1290410302eSdanielk1977#     then the contents of the backup database should be as they were
1300410302eSdanielk1977#     before the operation was started.
1310410302eSdanielk1977#
1320410302eSdanielk1977# The following factors are varied:
1330410302eSdanielk1977#
1340410302eSdanielk1977#   * Destination database is initially larger than the source database, OR
1350410302eSdanielk1977#   * Destination database is initially smaller than the source database.
1360410302eSdanielk1977#
1370410302eSdanielk1977#   * IO errors are transient, OR
1380410302eSdanielk1977#   * IO errors are persistent.
1390410302eSdanielk1977#
1400410302eSdanielk1977#   * Destination page-size is smaller than the source.
1410410302eSdanielk1977#   * Destination page-size is the same as the source.
1420410302eSdanielk1977#   * Destination page-size is larger than the source.
1430410302eSdanielk1977#
1440410302eSdanielk1977
1450410302eSdanielk1977set iTest 1
1460410302eSdanielk1977foreach bPersist {0 1} {
1470410302eSdanielk1977foreach iDestPagesize {512 1024 4096} {
1480410302eSdanielk1977foreach zSetupBak [list "" {populate_database ddb 1}] {
1490410302eSdanielk1977
1500410302eSdanielk1977  incr iTest
1510410302eSdanielk1977  set bStop 0
1520410302eSdanielk1977for {set iError 1} {$bStop == 0} {incr iError} {
1530410302eSdanielk1977  # Disable IO error simulation.
1540410302eSdanielk1977  clear_ioerr_simulation
1550410302eSdanielk1977
1560410302eSdanielk1977  catch { ddb close }
1570410302eSdanielk1977  catch { sdb close }
158fda06befSmistachkin  catch { forcedelete test.db }
159fda06befSmistachkin  catch { forcedelete bak.db }
1600410302eSdanielk1977
1610410302eSdanielk1977  # Open the source and destination databases.
1620410302eSdanielk1977  sqlite3 sdb test.db
1630410302eSdanielk1977  sqlite3 ddb bak.db
1640410302eSdanielk1977
1650410302eSdanielk1977  # Step 1: Populate the source and destination databases.
1660410302eSdanielk1977  populate_database sdb
1670410302eSdanielk1977  ddb eval "PRAGMA page_size = $iDestPagesize"
1680410302eSdanielk1977  ddb eval "PRAGMA cache_size = 10"
1690410302eSdanielk1977  eval $zSetupBak
1700410302eSdanielk1977
1710410302eSdanielk1977  # Step 2: Open the backup process.
1720410302eSdanielk1977  sqlite3_backup B ddb main sdb main
1730410302eSdanielk1977
1740410302eSdanielk1977  # Enable IO error simulation.
1750410302eSdanielk1977  set ::sqlite_io_error_pending $iError
1760410302eSdanielk1977  set ::sqlite_io_error_persist $bPersist
1770410302eSdanielk1977
1780410302eSdanielk1977  # Step 3: Partially backup the database. If an IO error occurs, check
1790410302eSdanielk1977  # a few things then skip to the next iteration of the loop.
1800410302eSdanielk1977  #
1810410302eSdanielk1977  set rc [B step 100]
1820410302eSdanielk1977  if {$::sqlite_io_error_hardhit} {
1830410302eSdanielk1977
1840410302eSdanielk1977    do_test backup_ioerr-$iTest.$iError.1 {
1850410302eSdanielk1977      string match SQLITE_IOERR* $rc
1860410302eSdanielk1977    } {1}
1870410302eSdanielk1977    do_test backup_ioerr-$iTest.$iError.2 {
1880410302eSdanielk1977      list [sqlite3_errcode ddb] [sqlite3_errmsg ddb]
1890410302eSdanielk1977    } {SQLITE_OK {not an error}}
1900410302eSdanielk1977
1910410302eSdanielk1977    set rc [B finish]
1920410302eSdanielk1977    do_test backup_ioerr-$iTest.$iError.3 {
1930410302eSdanielk1977      string match SQLITE_IOERR* $rc
1940410302eSdanielk1977    } {1}
1950410302eSdanielk1977
1960410302eSdanielk1977    do_test backup_ioerr-$iTest.$iError.4 {
1970410302eSdanielk1977      sqlite3_errmsg ddb
1980410302eSdanielk1977    } {disk I/O error}
1990410302eSdanielk1977
2000410302eSdanielk1977    clear_ioerr_simulation
2010410302eSdanielk1977    sqlite3 ddb bak.db
2020410302eSdanielk1977    integrity_check backup_ioerr-$iTest.$iError.5 ddb
2030410302eSdanielk1977
2040410302eSdanielk1977    continue
2050410302eSdanielk1977  }
2060410302eSdanielk1977
2070410302eSdanielk1977  # No IO error was encountered during step 3. Check that backup_step()
2080410302eSdanielk1977  # returned SQLITE_OK before proceding.
2090410302eSdanielk1977  do_test backup_ioerr-$iTest.$iError.6 {
2100410302eSdanielk1977    expr {$rc eq "SQLITE_OK"}
2110410302eSdanielk1977  } {1}
2120410302eSdanielk1977
2130410302eSdanielk1977  # Step 4: Write to the source database.
2140410302eSdanielk1977  set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb]
2150410302eSdanielk1977
2160410302eSdanielk1977  if {[lindex $rc 0] && $::sqlite_io_error_persist==0} {
217*48864df9Smistachkin    # The IO error occurred while updating the source database. In this
2180410302eSdanielk1977    # case the backup should be able to continue.
2190410302eSdanielk1977    set rc [B step 5000]
2200410302eSdanielk1977    if { $rc != "SQLITE_IOERR_UNLOCK" } {
2210410302eSdanielk1977      do_test backup_ioerr-$iTest.$iError.7 {
2220410302eSdanielk1977        list [B step 5000] [B finish]
2230410302eSdanielk1977      } {SQLITE_DONE SQLITE_OK}
2240410302eSdanielk1977
2250410302eSdanielk1977      clear_ioerr_simulation
2260410302eSdanielk1977      test_contents backup_ioerr-$iTest.$iError.8 ddb main sdb main
2270410302eSdanielk1977      integrity_check backup_ioerr-$iTest.$iError.9 ddb
2280410302eSdanielk1977    } else {
2290410302eSdanielk1977      do_test backup_ioerr-$iTest.$iError.10 {
2300410302eSdanielk1977        B finish
2310410302eSdanielk1977      } {SQLITE_IOERR_UNLOCK}
2320410302eSdanielk1977    }
2330410302eSdanielk1977
2340410302eSdanielk1977    clear_ioerr_simulation
2350410302eSdanielk1977    sqlite3 ddb bak.db
2360410302eSdanielk1977    integrity_check backup_ioerr-$iTest.$iError.11 ddb
2370410302eSdanielk1977
2380410302eSdanielk1977    continue
2390410302eSdanielk1977  }
2400410302eSdanielk1977
2410410302eSdanielk1977  # Step 5: Finish the backup operation. If an IO error occurs, check that
2420410302eSdanielk1977  # it is reported correctly and skip to the next iteration of the loop.
2430410302eSdanielk1977  #
2440410302eSdanielk1977  set rc [B step 5000]
2450410302eSdanielk1977  if {$rc != "SQLITE_DONE"} {
2460410302eSdanielk1977    do_test backup_ioerr-$iTest.$iError.12 {
2470410302eSdanielk1977      string match SQLITE_IOERR* $rc
2480410302eSdanielk1977    } {1}
2490410302eSdanielk1977    do_test backup_ioerr-$iTest.$iError.13 {
2500410302eSdanielk1977      list [sqlite3_errcode ddb] [sqlite3_errmsg ddb]
2510410302eSdanielk1977    } {SQLITE_OK {not an error}}
2520410302eSdanielk1977
2530410302eSdanielk1977    set rc [B finish]
2540410302eSdanielk1977    do_test backup_ioerr-$iTest.$iError.14 {
2550410302eSdanielk1977      string match SQLITE_IOERR* $rc
2560410302eSdanielk1977    } {1}
2570410302eSdanielk1977    do_test backup_ioerr-$iTest.$iError.15 {
2580410302eSdanielk1977      sqlite3_errmsg ddb
2590410302eSdanielk1977    } {disk I/O error}
2600410302eSdanielk1977
2610410302eSdanielk1977    clear_ioerr_simulation
2620410302eSdanielk1977    sqlite3 ddb bak.db
2630410302eSdanielk1977    integrity_check backup_ioerr-$iTest.$iError.16 ddb
2640410302eSdanielk1977
2650410302eSdanielk1977    continue
2660410302eSdanielk1977  }
2670410302eSdanielk1977
2680410302eSdanielk1977  # The backup was successfully completed.
2690410302eSdanielk1977  #
2700410302eSdanielk1977  do_test backup_ioerr-$iTest.$iError.17 {
2710410302eSdanielk1977    list [set rc] [B finish]
2720410302eSdanielk1977  } {SQLITE_DONE SQLITE_OK}
2730410302eSdanielk1977
2740410302eSdanielk1977  clear_ioerr_simulation
2750410302eSdanielk1977  sqlite3 sdb test.db
2760410302eSdanielk1977  sqlite3 ddb bak.db
2770410302eSdanielk1977
2780410302eSdanielk1977  test_contents backup_ioerr-$iTest.$iError.18 ddb main sdb main
2790410302eSdanielk1977  integrity_check backup_ioerr-$iTest.$iError.19 ddb
2800410302eSdanielk1977
2810410302eSdanielk1977  set bStop [expr $::sqlite_io_error_pending<=0]
2820410302eSdanielk1977}}}}
2830410302eSdanielk1977
2840410302eSdanielk1977catch { sdb close }
2850410302eSdanielk1977catch { ddb close }
2860410302eSdanielk1977finish_test
287