version bump to 4.6.1
[sqlcipher.git] / test / backup_ioerr.test
blobca3fd3240d778d1b6fd19b59a66ffcf7a66b9f78
1 # 2009 January 30
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
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.
15 # $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $
17 set testdir [file dirname $argv0]
18 source $testdir/tester.tcl
20 proc data_checksum {db file} { 
21   $db one "SELECT md5sum(a, b) FROM ${file}.t1" 
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]
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.
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   }
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 } {}
66 # Turn off IO error simulation.
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
75 #--------------------------------------------------------------------
76 # The following procedure runs with SQLite's IO error simulation 
77 # enabled.
79 #   1) Start with a reasonably sized database. One that includes the
80 #      pending-byte (locking) page.
82 #   2) Open a backup process. Set the cache-size for the destination
83 #      database to 10 pages only.
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().
89 #      If an IO error occurs, verify that:
91 #      * the call to backup_step() returns an SQLITE_IOERR_XXX error code.
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.
97 #      * the call to backup_finish() returns an SQLITE_IOERR_XXX error code.
99 #      * following the call to backup_finish(), the destination database
100 #        handle has been populated with an error code and error message.
102 #   4) Write to the database via the source database connection. Check 
103 #      that:
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.
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).
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().
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.
122 #   6) Finish the backup process.
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.
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.
132 # The following factors are varied:
134 #   * Destination database is initially larger than the source database, OR
135 #   * Destination database is initially smaller than the source database.
137 #   * IO errors are transient, OR
138 #   * IO errors are persistent.
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.
145 set iTest 1
146 foreach bPersist {0 1} {
147 foreach iDestPagesize {512 1024 4096} {
148 foreach zSetupBak [list "" {populate_database ddb 1}] {
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
156   catch { ddb close }
157   catch { sdb close }
158   catch { forcedelete test.db }
159   catch { forcedelete bak.db }
161   # Open the source and destination databases.
162   sqlite3 sdb test.db
163   sqlite3 ddb bak.db
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
171   # Step 2: Open the backup process.
172   sqlite3_backup B ddb main sdb main
174   # Enable IO error simulation.
175   set ::sqlite_io_error_pending $iError
176   set ::sqlite_io_error_persist $bPersist
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} {
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}}
191     set rc [B finish]
192     do_test backup_ioerr-$iTest.$iError.3 {
193       string match SQLITE_IOERR* $rc
194     } {1}
196     do_test backup_ioerr-$iTest.$iError.4 {
197       sqlite3_errmsg ddb
198     } {disk I/O error}
200     clear_ioerr_simulation
201     sqlite3 ddb bak.db
202     integrity_check backup_ioerr-$iTest.$iError.5 ddb
204     continue
205   }
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}
213   # Step 4: Write to the source database.
214   set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb]
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}
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     }
234     clear_ioerr_simulation
235     sqlite3 ddb bak.db
236     integrity_check backup_ioerr-$iTest.$iError.11 ddb
238     continue
239   }
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}}
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}
261     clear_ioerr_simulation
262     sqlite3 ddb bak.db
263     integrity_check backup_ioerr-$iTest.$iError.16 ddb
265     continue
266   }
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}
274   clear_ioerr_simulation
275   sqlite3 sdb test.db
276   sqlite3 ddb bak.db
278   test_contents backup_ioerr-$iTest.$iError.18 ddb main sdb main
279   integrity_check backup_ioerr-$iTest.$iError.19 ddb
281   set bStop [expr $::sqlite_io_error_pending<=0]
282 }}}}
284 catch { sdb close }
285 catch { ddb close }
286 finish_test