diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:07:11 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:07:11 +0000 |
commit | 63847496f14c813a5d80efd5b7de0f1294ffe1e3 (patch) | |
tree | 01c7571c7c762ceee70638549a99834fdd7c411b /test/backup_ioerr.test | |
parent | Initial commit. (diff) | |
download | sqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.tar.xz sqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.zip |
Adding upstream version 3.45.1.upstream/3.45.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/backup_ioerr.test')
-rw-r--r-- | test/backup_ioerr.test | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/test/backup_ioerr.test b/test/backup_ioerr.test new file mode 100644 index 0000000..ca3fd32 --- /dev/null +++ b/test/backup_ioerr.test @@ -0,0 +1,286 @@ +# 2009 January 30 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the handling of IO errors by the +# sqlite3_backup_XXX APIs. +# +# $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +proc data_checksum {db file} { + $db one "SELECT md5sum(a, b) FROM ${file}.t1" +} +proc test_contents {name db1 file1 db2 file2} { + $db2 eval {select * from sqlite_master} + $db1 eval {select * from sqlite_master} + set checksum [data_checksum $db2 $file2] + uplevel [list do_test $name [list data_checksum $db1 $file1] $checksum] +} + +#-------------------------------------------------------------------- +# This proc creates a database of approximately 290 pages. Depending +# on whether or not auto-vacuum is configured. Test cases backup_ioerr-1.* +# verify nothing more than this assumption. +# +proc populate_database {db {xtra_large 0}} { + execsql { + BEGIN; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, randstr(1000,1000)); + INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1; + INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1; + INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1; + INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1; + INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1; + INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1; + CREATE INDEX i1 ON t1(b); + COMMIT; + } $db + if {$xtra_large} { + execsql { INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1 } $db + } +} +do_test backup_ioerr-1.1 { + populate_database db + set nPage [expr {[file size test.db] / 1024}] + expr {$nPage>130 && $nPage<160} +} {1} +do_test backup_ioerr-1.2 { + expr {[file size test.db] > $sqlite_pending_byte} +} {1} +do_test backup_ioerr-1.3 { + db close + forcedelete test.db +} {} + +# Turn off IO error simulation. +# +proc clear_ioerr_simulation {} { + set ::sqlite_io_error_hit 0 + set ::sqlite_io_error_hardhit 0 + set ::sqlite_io_error_pending 0 + set ::sqlite_io_error_persist 0 +} + +#-------------------------------------------------------------------- +# The following procedure runs with SQLite's IO error simulation +# enabled. +# +# 1) Start with a reasonably sized database. One that includes the +# pending-byte (locking) page. +# +# 2) Open a backup process. Set the cache-size for the destination +# database to 10 pages only. +# +# 3) Step the backup process N times to partially backup the database +# file. If an IO error is reported, then the backup process is +# concluded with a call to backup_finish(). +# +# If an IO error occurs, verify that: +# +# * the call to backup_step() returns an SQLITE_IOERR_XXX error code. +# +# * after the failed call to backup_step() but before the call to +# backup_finish() the destination database handle error code and +# error message remain unchanged. +# +# * the call to backup_finish() returns an SQLITE_IOERR_XXX error code. +# +# * following the call to backup_finish(), the destination database +# handle has been populated with an error code and error message. +# +# 4) Write to the database via the source database connection. Check +# that: +# +# * If an IO error occurs while writing the source database, the +# write operation should report an IO error. The backup should +# proceed as normal. +# +# * If an IO error occurs while updating the backup, the write +# operation should proceed normally. The error should be reported +# from the next call to backup_step() (in step 5 of this test +# procedure). +# +# 5) Step the backup process to finish the backup. If an IO error is +# reported, then the backup process is concluded with a call to +# backup_finish(). +# +# Test that if an IO error occurs, or if one occurred while updating +# the backup database during step 4, then the conditions listed +# under step 3 are all true. +# +# 6) Finish the backup process. +# +# * If the backup succeeds (backup_finish() returns SQLITE_OK), then +# the contents of the backup database should match that of the +# source database. +# +# * If the backup fails (backup_finish() returns other than SQLITE_OK), +# then the contents of the backup database should be as they were +# before the operation was started. +# +# The following factors are varied: +# +# * Destination database is initially larger than the source database, OR +# * Destination database is initially smaller than the source database. +# +# * IO errors are transient, OR +# * IO errors are persistent. +# +# * Destination page-size is smaller than the source. +# * Destination page-size is the same as the source. +# * Destination page-size is larger than the source. +# + +set iTest 1 +foreach bPersist {0 1} { +foreach iDestPagesize {512 1024 4096} { +foreach zSetupBak [list "" {populate_database ddb 1}] { + + incr iTest + set bStop 0 +for {set iError 1} {$bStop == 0} {incr iError} { + # Disable IO error simulation. + clear_ioerr_simulation + + catch { ddb close } + catch { sdb close } + catch { forcedelete test.db } + catch { forcedelete bak.db } + + # Open the source and destination databases. + sqlite3 sdb test.db + sqlite3 ddb bak.db + + # Step 1: Populate the source and destination databases. + populate_database sdb + ddb eval "PRAGMA page_size = $iDestPagesize" + ddb eval "PRAGMA cache_size = 10" + eval $zSetupBak + + # Step 2: Open the backup process. + sqlite3_backup B ddb main sdb main + + # Enable IO error simulation. + set ::sqlite_io_error_pending $iError + set ::sqlite_io_error_persist $bPersist + + # Step 3: Partially backup the database. If an IO error occurs, check + # a few things then skip to the next iteration of the loop. + # + set rc [B step 100] + if {$::sqlite_io_error_hardhit} { + + do_test backup_ioerr-$iTest.$iError.1 { + string match SQLITE_IOERR* $rc + } {1} + do_test backup_ioerr-$iTest.$iError.2 { + list [sqlite3_errcode ddb] [sqlite3_errmsg ddb] + } {SQLITE_OK {not an error}} + + set rc [B finish] + do_test backup_ioerr-$iTest.$iError.3 { + string match SQLITE_IOERR* $rc + } {1} + + do_test backup_ioerr-$iTest.$iError.4 { + sqlite3_errmsg ddb + } {disk I/O error} + + clear_ioerr_simulation + sqlite3 ddb bak.db + integrity_check backup_ioerr-$iTest.$iError.5 ddb + + continue + } + + # No IO error was encountered during step 3. Check that backup_step() + # returned SQLITE_OK before proceding. + do_test backup_ioerr-$iTest.$iError.6 { + expr {$rc eq "SQLITE_OK"} + } {1} + + # Step 4: Write to the source database. + set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb] + + if {[lindex $rc 0] && $::sqlite_io_error_persist==0} { + # The IO error occurred while updating the source database. In this + # case the backup should be able to continue. + set rc [B step 5000] + if { $rc != "SQLITE_IOERR_UNLOCK" } { + do_test backup_ioerr-$iTest.$iError.7 { + list [B step 5000] [B finish] + } {SQLITE_DONE SQLITE_OK} + + clear_ioerr_simulation + test_contents backup_ioerr-$iTest.$iError.8 ddb main sdb main + integrity_check backup_ioerr-$iTest.$iError.9 ddb + } else { + do_test backup_ioerr-$iTest.$iError.10 { + B finish + } {SQLITE_IOERR_UNLOCK} + } + + clear_ioerr_simulation + sqlite3 ddb bak.db + integrity_check backup_ioerr-$iTest.$iError.11 ddb + + continue + } + + # Step 5: Finish the backup operation. If an IO error occurs, check that + # it is reported correctly and skip to the next iteration of the loop. + # + set rc [B step 5000] + if {$rc != "SQLITE_DONE"} { + do_test backup_ioerr-$iTest.$iError.12 { + string match SQLITE_IOERR* $rc + } {1} + do_test backup_ioerr-$iTest.$iError.13 { + list [sqlite3_errcode ddb] [sqlite3_errmsg ddb] + } {SQLITE_OK {not an error}} + + set rc [B finish] + do_test backup_ioerr-$iTest.$iError.14 { + string match SQLITE_IOERR* $rc + } {1} + do_test backup_ioerr-$iTest.$iError.15 { + sqlite3_errmsg ddb + } {disk I/O error} + + clear_ioerr_simulation + sqlite3 ddb bak.db + integrity_check backup_ioerr-$iTest.$iError.16 ddb + + continue + } + + # The backup was successfully completed. + # + do_test backup_ioerr-$iTest.$iError.17 { + list [set rc] [B finish] + } {SQLITE_DONE SQLITE_OK} + + clear_ioerr_simulation + sqlite3 sdb test.db + sqlite3 ddb bak.db + + test_contents backup_ioerr-$iTest.$iError.18 ddb main sdb main + integrity_check backup_ioerr-$iTest.$iError.19 ddb + + set bStop [expr $::sqlite_io_error_pending<=0] +}}}} + +catch { sdb close } +catch { ddb close } +finish_test |