backup_ioerr.test 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. set testdir [file dirname $argv0]
  17. source $testdir/tester.tcl
  18. proc data_checksum {db file} {
  19. $db one "SELECT md5sum(a, b) FROM ${file}.t1"
  20. }
  21. proc test_contents {name db1 file1 db2 file2} {
  22. $db2 eval {select * from sqlite_master}
  23. $db1 eval {select * from sqlite_master}
  24. set checksum [data_checksum $db2 $file2]
  25. uplevel [list do_test $name [list data_checksum $db1 $file1] $checksum]
  26. }
  27. #--------------------------------------------------------------------
  28. # This proc creates a database of approximately 290 pages. Depending
  29. # on whether or not auto-vacuum is configured. Test cases backup_ioerr-1.*
  30. # verify nothing more than this assumption.
  31. #
  32. proc populate_database {db {xtra_large 0}} {
  33. execsql {
  34. BEGIN;
  35. CREATE TABLE t1(a, b);
  36. INSERT INTO t1 VALUES(1, randstr(1000,1000));
  37. INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1;
  38. INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1;
  39. INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1;
  40. INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1;
  41. INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1;
  42. INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1;
  43. CREATE INDEX i1 ON t1(b);
  44. COMMIT;
  45. } $db
  46. if {$xtra_large} {
  47. execsql { INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1 } $db
  48. }
  49. }
  50. do_test backup_ioerr-1.1 {
  51. populate_database db
  52. set nPage [expr {[file size test.db] / 1024}]
  53. expr {$nPage>130 && $nPage<160}
  54. } {1}
  55. do_test backup_ioerr-1.2 {
  56. expr {[file size test.db] > $sqlite_pending_byte}
  57. } {1}
  58. do_test backup_ioerr-1.3 {
  59. db close
  60. forcedelete test.db
  61. } {}
  62. # Turn off IO error simulation.
  63. #
  64. proc clear_ioerr_simulation {} {
  65. set ::sqlite_io_error_hit 0
  66. set ::sqlite_io_error_hardhit 0
  67. set ::sqlite_io_error_pending 0
  68. set ::sqlite_io_error_persist 0
  69. }
  70. #--------------------------------------------------------------------
  71. # The following procedure runs with SQLite's IO error simulation
  72. # enabled.
  73. #
  74. # 1) Start with a reasonably sized database. One that includes the
  75. # pending-byte (locking) page.
  76. #
  77. # 2) Open a backup process. Set the cache-size for the destination
  78. # database to 10 pages only.
  79. #
  80. # 3) Step the backup process N times to partially backup the database
  81. # file. If an IO error is reported, then the backup process is
  82. # concluded with a call to backup_finish().
  83. #
  84. # If an IO error occurs, verify that:
  85. #
  86. # * the call to backup_step() returns an SQLITE_IOERR_XXX error code.
  87. #
  88. # * after the failed call to backup_step() but before the call to
  89. # backup_finish() the destination database handle error code and
  90. # error message remain unchanged.
  91. #
  92. # * the call to backup_finish() returns an SQLITE_IOERR_XXX error code.
  93. #
  94. # * following the call to backup_finish(), the destination database
  95. # handle has been populated with an error code and error message.
  96. #
  97. # 4) Write to the database via the source database connection. Check
  98. # that:
  99. #
  100. # * If an IO error occurs while writing the source database, the
  101. # write operation should report an IO error. The backup should
  102. # proceed as normal.
  103. #
  104. # * If an IO error occurs while updating the backup, the write
  105. # operation should proceed normally. The error should be reported
  106. # from the next call to backup_step() (in step 5 of this test
  107. # procedure).
  108. #
  109. # 5) Step the backup process to finish the backup. If an IO error is
  110. # reported, then the backup process is concluded with a call to
  111. # backup_finish().
  112. #
  113. # Test that if an IO error occurs, or if one occurred while updating
  114. # the backup database during step 4, then the conditions listed
  115. # under step 3 are all true.
  116. #
  117. # 6) Finish the backup process.
  118. #
  119. # * If the backup succeeds (backup_finish() returns SQLITE_OK), then
  120. # the contents of the backup database should match that of the
  121. # source database.
  122. #
  123. # * If the backup fails (backup_finish() returns other than SQLITE_OK),
  124. # then the contents of the backup database should be as they were
  125. # before the operation was started.
  126. #
  127. # The following factors are varied:
  128. #
  129. # * Destination database is initially larger than the source database, OR
  130. # * Destination database is initially smaller than the source database.
  131. #
  132. # * IO errors are transient, OR
  133. # * IO errors are persistent.
  134. #
  135. # * Destination page-size is smaller than the source.
  136. # * Destination page-size is the same as the source.
  137. # * Destination page-size is larger than the source.
  138. #
  139. set iTest 1
  140. foreach bPersist {0 1} {
  141. foreach iDestPagesize {512 1024 4096} {
  142. foreach zSetupBak [list "" {populate_database ddb 1}] {
  143. incr iTest
  144. set bStop 0
  145. for {set iError 1} {$bStop == 0} {incr iError} {
  146. # Disable IO error simulation.
  147. clear_ioerr_simulation
  148. catch { ddb close }
  149. catch { sdb close }
  150. catch { forcedelete test.db }
  151. catch { forcedelete bak.db }
  152. # Open the source and destination databases.
  153. sqlite3 sdb test.db
  154. sqlite3 ddb bak.db
  155. # Step 1: Populate the source and destination databases.
  156. populate_database sdb
  157. ddb eval "PRAGMA page_size = $iDestPagesize"
  158. ddb eval "PRAGMA cache_size = 10"
  159. eval $zSetupBak
  160. # Step 2: Open the backup process.
  161. sqlite3_backup B ddb main sdb main
  162. # Enable IO error simulation.
  163. set ::sqlite_io_error_pending $iError
  164. set ::sqlite_io_error_persist $bPersist
  165. # Step 3: Partially backup the database. If an IO error occurs, check
  166. # a few things then skip to the next iteration of the loop.
  167. #
  168. set rc [B step 100]
  169. if {$::sqlite_io_error_hardhit} {
  170. do_test backup_ioerr-$iTest.$iError.1 {
  171. string match SQLITE_IOERR* $rc
  172. } {1}
  173. do_test backup_ioerr-$iTest.$iError.2 {
  174. list [sqlite3_errcode ddb] [sqlite3_errmsg ddb]
  175. } {SQLITE_OK {not an error}}
  176. set rc [B finish]
  177. do_test backup_ioerr-$iTest.$iError.3 {
  178. string match SQLITE_IOERR* $rc
  179. } {1}
  180. do_test backup_ioerr-$iTest.$iError.4 {
  181. sqlite3_errmsg ddb
  182. } {disk I/O error}
  183. clear_ioerr_simulation
  184. sqlite3 ddb bak.db
  185. integrity_check backup_ioerr-$iTest.$iError.5 ddb
  186. continue
  187. }
  188. # No IO error was encountered during step 3. Check that backup_step()
  189. # returned SQLITE_OK before proceding.
  190. do_test backup_ioerr-$iTest.$iError.6 {
  191. expr {$rc eq "SQLITE_OK"}
  192. } {1}
  193. # Step 4: Write to the source database.
  194. set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb]
  195. if {[lindex $rc 0] && $::sqlite_io_error_persist==0} {
  196. # The IO error occurred while updating the source database. In this
  197. # case the backup should be able to continue.
  198. set rc [B step 5000]
  199. if { $rc != "SQLITE_IOERR_UNLOCK" } {
  200. do_test backup_ioerr-$iTest.$iError.7 {
  201. list [B step 5000] [B finish]
  202. } {SQLITE_DONE SQLITE_OK}
  203. clear_ioerr_simulation
  204. test_contents backup_ioerr-$iTest.$iError.8 ddb main sdb main
  205. integrity_check backup_ioerr-$iTest.$iError.9 ddb
  206. } else {
  207. do_test backup_ioerr-$iTest.$iError.10 {
  208. B finish
  209. } {SQLITE_IOERR_UNLOCK}
  210. }
  211. clear_ioerr_simulation
  212. sqlite3 ddb bak.db
  213. integrity_check backup_ioerr-$iTest.$iError.11 ddb
  214. continue
  215. }
  216. # Step 5: Finish the backup operation. If an IO error occurs, check that
  217. # it is reported correctly and skip to the next iteration of the loop.
  218. #
  219. set rc [B step 5000]
  220. if {$rc != "SQLITE_DONE"} {
  221. do_test backup_ioerr-$iTest.$iError.12 {
  222. string match SQLITE_IOERR* $rc
  223. } {1}
  224. do_test backup_ioerr-$iTest.$iError.13 {
  225. list [sqlite3_errcode ddb] [sqlite3_errmsg ddb]
  226. } {SQLITE_OK {not an error}}
  227. set rc [B finish]
  228. do_test backup_ioerr-$iTest.$iError.14 {
  229. string match SQLITE_IOERR* $rc
  230. } {1}
  231. do_test backup_ioerr-$iTest.$iError.15 {
  232. sqlite3_errmsg ddb
  233. } {disk I/O error}
  234. clear_ioerr_simulation
  235. sqlite3 ddb bak.db
  236. integrity_check backup_ioerr-$iTest.$iError.16 ddb
  237. continue
  238. }
  239. # The backup was successfully completed.
  240. #
  241. do_test backup_ioerr-$iTest.$iError.17 {
  242. list [set rc] [B finish]
  243. } {SQLITE_DONE SQLITE_OK}
  244. clear_ioerr_simulation
  245. sqlite3 sdb test.db
  246. sqlite3 ddb bak.db
  247. test_contents backup_ioerr-$iTest.$iError.18 ddb main sdb main
  248. integrity_check backup_ioerr-$iTest.$iError.19 ddb
  249. set bStop [expr $::sqlite_io_error_pending<=0]
  250. }}}}
  251. catch { sdb close }
  252. catch { ddb close }
  253. finish_test