AryaWu/sqlite
0
1# 2009 January 302#3# The author disclaims copyright to this source code. In place of4# 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. The12# focus of this file is testing the handling of IO errors by the13# sqlite3_backup_XXX APIs.14#15# $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $16 17set testdir [file dirname $argv0]18source $testdir/tester.tcl19 20proc data_checksum {db file} { 21 $db one "SELECT md5sum(a, b) FROM ${file}.t1" 22}23proc 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. Depending32# on whether or not auto-vacuum is configured. Test cases backup_ioerr-1.*33# verify nothing more than this assumption.34#35proc 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 } $db49 if {$xtra_large} {50 execsql { INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1 } $db51 }52}53do_test backup_ioerr-1.1 {54 populate_database db55 set nPage [expr {[file size test.db] / 1024}]56 expr {$nPage>130 && $nPage<160}57} {1}58do_test backup_ioerr-1.2 {59 expr {[file size test.db] > $sqlite_pending_byte}60} {1}61do_test backup_ioerr-1.3 {62 db close63 forcedelete test.db64} {}65 66# Turn off IO error simulation.67#68proc clear_ioerr_simulation {} {69 set ::sqlite_io_error_hit 070 set ::sqlite_io_error_hardhit 071 set ::sqlite_io_error_pending 072 set ::sqlite_io_error_persist 073}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 the80# pending-byte (locking) page.81#82# 2) Open a backup process. Set the cache-size for the destination83# database to 10 pages only.84#85# 3) Step the backup process N times to partially backup the database86# file. If an IO error is reported, then the backup process is87# 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 to94# 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 database100# 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, the106# 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 reported111# from the next call to backup_step() (in step 5 of this test112# 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 updating119# the backup database during step 4, then the conditions listed120# under step 3 are all true.121#122# 6) Finish the backup process.123#124# * If the backup succeeds (backup_finish() returns SQLITE_OK), then125# the contents of the backup database should match that of the126# 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, OR135# * Destination database is initially smaller than the source database.136#137# * IO errors are transient, OR138# * 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 145set iTest 1146foreach bPersist {0 1} {147foreach iDestPagesize {512 1024 4096} {148foreach zSetupBak [list "" {populate_database ddb 1}] {149 150 incr iTest151 set bStop 0152for {set iError 1} {$bStop == 0} {incr iError} {153 # Disable IO error simulation.154 clear_ioerr_simulation155 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.db163 sqlite3 ddb bak.db164 165 # Step 1: Populate the source and destination databases.166 populate_database sdb167 ddb eval "PRAGMA page_size = $iDestPagesize"168 ddb eval "PRAGMA cache_size = 10"169 eval $zSetupBak170 171 # Step 2: Open the backup process.172 sqlite3_backup B ddb main sdb main173 174 # Enable IO error simulation.175 set ::sqlite_io_error_pending $iError176 set ::sqlite_io_error_persist $bPersist177 178 # Step 3: Partially backup the database. If an IO error occurs, check179 # 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* $rc186 } {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* $rc194 } {1}195 196 do_test backup_ioerr-$iTest.$iError.4 {197 sqlite3_errmsg ddb198 } {disk I/O error}199 200 clear_ioerr_simulation201 sqlite3 ddb bak.db202 integrity_check backup_ioerr-$iTest.$iError.5 ddb203 204 continue205 }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 this218 # 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_simulation226 test_contents backup_ioerr-$iTest.$iError.8 ddb main sdb main227 integrity_check backup_ioerr-$iTest.$iError.9 ddb228 } else {229 do_test backup_ioerr-$iTest.$iError.10 {230 B finish231 } {SQLITE_IOERR_UNLOCK}232 }233 234 clear_ioerr_simulation235 sqlite3 ddb bak.db236 integrity_check backup_ioerr-$iTest.$iError.11 ddb237 238 continue239 }240 241 # Step 5: Finish the backup operation. If an IO error occurs, check that242 # 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* $rc248 } {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* $rc256 } {1}257 do_test backup_ioerr-$iTest.$iError.15 {258 sqlite3_errmsg ddb259 } {disk I/O error}260 261 clear_ioerr_simulation262 sqlite3 ddb bak.db263 integrity_check backup_ioerr-$iTest.$iError.16 ddb264 265 continue266 }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_simulation275 sqlite3 sdb test.db276 sqlite3 ddb bak.db277 278 test_contents backup_ioerr-$iTest.$iError.18 ddb main sdb main279 integrity_check backup_ioerr-$iTest.$iError.19 ddb280 281 set bStop [expr $::sqlite_io_error_pending<=0]282}}}}283 284catch { sdb close }285catch { ddb close }286finish_test287 