| OLD | NEW |
| (Empty) |
| 1 # 2009 January 8 | |
| 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 # | |
| 12 # This test verifies a couple of specific potential data corruption | |
| 13 # scenarios involving crashes or power failures. | |
| 14 # | |
| 15 # Later: Also, some other specific scenarios required for coverage | |
| 16 # testing that do not lead to corruption. | |
| 17 # | |
| 18 # $Id: crash8.test,v 1.4 2009/01/11 00:44:48 drh Exp $ | |
| 19 | |
| 20 | |
| 21 set testdir [file dirname $argv0] | |
| 22 source $testdir/tester.tcl | |
| 23 | |
| 24 ifcapable !crashtest { | |
| 25 finish_test | |
| 26 return | |
| 27 } | |
| 28 | |
| 29 do_test crash8-1.1 { | |
| 30 execsql { | |
| 31 PRAGMA auto_vacuum=OFF; | |
| 32 CREATE TABLE t1(a, b); | |
| 33 CREATE INDEX i1 ON t1(a, b); | |
| 34 INSERT INTO t1 VALUES(1, randstr(1000,1000)); | |
| 35 INSERT INTO t1 VALUES(2, randstr(1000,1000)); | |
| 36 INSERT INTO t1 VALUES(3, randstr(1000,1000)); | |
| 37 INSERT INTO t1 VALUES(4, randstr(1000,1000)); | |
| 38 INSERT INTO t1 VALUES(5, randstr(1000,1000)); | |
| 39 INSERT INTO t1 VALUES(6, randstr(1000,1000)); | |
| 40 CREATE TABLE t2(a, b); | |
| 41 CREATE TABLE t3(a, b); | |
| 42 CREATE TABLE t4(a, b); | |
| 43 CREATE TABLE t5(a, b); | |
| 44 CREATE TABLE t6(a, b); | |
| 45 CREATE TABLE t7(a, b); | |
| 46 CREATE TABLE t8(a, b); | |
| 47 CREATE TABLE t9(a, b); | |
| 48 CREATE TABLE t10(a, b); | |
| 49 PRAGMA integrity_check | |
| 50 } | |
| 51 } {ok} | |
| 52 | |
| 53 | |
| 54 # Potential corruption scenario 1. A second process opens the database | |
| 55 # and modifies a large portion of it. It then opens a second transaction | |
| 56 # and modifies a small part of the database, but crashes before it commits | |
| 57 # the transaction. | |
| 58 # | |
| 59 # When the first process accessed the database again, it was rolling back | |
| 60 # the aborted transaction, but was not purging its in-memory cache (which | |
| 61 # was loaded before the second process made its first, successful, | |
| 62 # modification). Producing an inconsistent cache. | |
| 63 # | |
| 64 do_test crash8-1.2 { | |
| 65 crashsql -delay 2 -file test.db { | |
| 66 PRAGMA cache_size = 10; | |
| 67 UPDATE t1 SET b = randstr(1000,1000); | |
| 68 INSERT INTO t9 VALUES(1, 2); | |
| 69 } | |
| 70 } {1 {child process exited abnormally}} | |
| 71 do_test crash8-1.3 { | |
| 72 execsql {PRAGMA integrity_check} | |
| 73 } {ok} | |
| 74 | |
| 75 # Potential corruption scenario 2. The second process, operating in | |
| 76 # persistent-journal mode, makes a large change to the database file | |
| 77 # with a small in-memory cache. Such that more than one journal-header | |
| 78 # was written to the file. It then opens a second transaction and makes | |
| 79 # a smaller change that requires only a single journal-header to be | |
| 80 # written to the journal file. The second change is such that the | |
| 81 # journal content written to the persistent journal file exactly overwrites | |
| 82 # the first journal-header and set of subsequent records written by the | |
| 83 # first, successful, change. The second process crashes before it can | |
| 84 # commit its second change. | |
| 85 # | |
| 86 # When the first process accessed the database again, it was rolling back | |
| 87 # the second aborted transaction, then continuing to rollback the second | |
| 88 # and subsequent journal-headers written by the first, successful, change. | |
| 89 # Database corruption. | |
| 90 # | |
| 91 do_test crash8.2.1 { | |
| 92 crashsql -delay 2 -file test.db { | |
| 93 PRAGMA journal_mode = persist; | |
| 94 PRAGMA cache_size = 10; | |
| 95 UPDATE t1 SET b = randstr(1000,1000); | |
| 96 PRAGMA cache_size = 100; | |
| 97 BEGIN; | |
| 98 INSERT INTO t2 VALUES('a', 'b'); | |
| 99 INSERT INTO t3 VALUES('a', 'b'); | |
| 100 INSERT INTO t4 VALUES('a', 'b'); | |
| 101 INSERT INTO t5 VALUES('a', 'b'); | |
| 102 INSERT INTO t6 VALUES('a', 'b'); | |
| 103 INSERT INTO t7 VALUES('a', 'b'); | |
| 104 INSERT INTO t8 VALUES('a', 'b'); | |
| 105 INSERT INTO t9 VALUES('a', 'b'); | |
| 106 INSERT INTO t10 VALUES('a', 'b'); | |
| 107 COMMIT; | |
| 108 } | |
| 109 } {1 {child process exited abnormally}} | |
| 110 | |
| 111 do_test crash8-2.3 { | |
| 112 execsql {PRAGMA integrity_check} | |
| 113 } {ok} | |
| 114 | |
| 115 proc read_file {zFile} { | |
| 116 set fd [open $zFile] | |
| 117 fconfigure $fd -translation binary | |
| 118 set zData [read $fd] | |
| 119 close $fd | |
| 120 return $zData | |
| 121 } | |
| 122 proc write_file {zFile zData} { | |
| 123 set fd [open $zFile w] | |
| 124 fconfigure $fd -translation binary | |
| 125 puts -nonewline $fd $zData | |
| 126 close $fd | |
| 127 } | |
| 128 | |
| 129 # The following tests check that SQLite will not roll back a hot-journal | |
| 130 # file if the sector-size field in the first journal file header is | |
| 131 # suspect. Definition of suspect: | |
| 132 # | |
| 133 # a) Not a power of 2, or (crash8-3.5) | |
| 134 # b) Greater than 0x01000000 (16MB), or (crash8-3.6) | |
| 135 # c) Less than 512. (crash8-3.7) | |
| 136 # | |
| 137 # Also test that SQLite will not rollback a hot-journal file with a | |
| 138 # suspect page-size. In this case "suspect" means: | |
| 139 # | |
| 140 # a) Not a power of 2, or | |
| 141 # b) Less than 512, or | |
| 142 # c) Greater than SQLITE_MAX_PAGE_SIZE | |
| 143 # | |
| 144 do_test crash8-3.1 { | |
| 145 list [file exists test.db-joural] [file exists test.db] | |
| 146 } {0 1} | |
| 147 do_test crash8-3.2 { | |
| 148 execsql { | |
| 149 PRAGMA synchronous = off; | |
| 150 BEGIN; | |
| 151 DELETE FROM t1; | |
| 152 SELECT count(*) FROM t1; | |
| 153 } | |
| 154 } {0} | |
| 155 do_test crash8-3.3 { | |
| 156 set zJournal [read_file test.db-journal] | |
| 157 execsql { | |
| 158 COMMIT; | |
| 159 SELECT count(*) FROM t1; | |
| 160 } | |
| 161 } {0} | |
| 162 do_test crash8-3.4 { | |
| 163 binary scan [string range $zJournal 20 23] I nSector | |
| 164 set nSector | |
| 165 } {512} | |
| 166 | |
| 167 do_test crash8-3.5 { | |
| 168 set zJournal2 [string replace $zJournal 20 23 [binary format I 513]] | |
| 169 write_file test.db-journal $zJournal2 | |
| 170 | |
| 171 execsql { | |
| 172 SELECT count(*) FROM t1; | |
| 173 PRAGMA integrity_check | |
| 174 } | |
| 175 } {0 ok} | |
| 176 do_test crash8-3.6 { | |
| 177 set zJournal2 [string replace $zJournal 20 23 [binary format I 0x2000000]] | |
| 178 write_file test.db-journal $zJournal2 | |
| 179 execsql { | |
| 180 SELECT count(*) FROM t1; | |
| 181 PRAGMA integrity_check | |
| 182 } | |
| 183 } {0 ok} | |
| 184 do_test crash8-3.7 { | |
| 185 set zJournal2 [string replace $zJournal 20 23 [binary format I 256]] | |
| 186 write_file test.db-journal $zJournal2 | |
| 187 execsql { | |
| 188 SELECT count(*) FROM t1; | |
| 189 PRAGMA integrity_check | |
| 190 } | |
| 191 } {0 ok} | |
| 192 | |
| 193 do_test crash8-3.8 { | |
| 194 set zJournal2 [string replace $zJournal 24 27 [binary format I 513]] | |
| 195 write_file test.db-journal $zJournal2 | |
| 196 | |
| 197 execsql { | |
| 198 SELECT count(*) FROM t1; | |
| 199 PRAGMA integrity_check | |
| 200 } | |
| 201 } {0 ok} | |
| 202 do_test crash8-3.9 { | |
| 203 set big [expr $SQLITE_MAX_PAGE_SIZE * 2] | |
| 204 set zJournal2 [string replace $zJournal 24 27 [binary format I $big]] | |
| 205 write_file test.db-journal $zJournal2 | |
| 206 execsql { | |
| 207 SELECT count(*) FROM t1; | |
| 208 PRAGMA integrity_check | |
| 209 } | |
| 210 } {0 ok} | |
| 211 do_test crash8-3.10 { | |
| 212 set zJournal2 [string replace $zJournal 24 27 [binary format I 256]] | |
| 213 write_file test.db-journal $zJournal2 | |
| 214 execsql { | |
| 215 SELECT count(*) FROM t1; | |
| 216 PRAGMA integrity_check | |
| 217 } | |
| 218 } {0 ok} | |
| 219 | |
| 220 do_test crash8-3.11 { | |
| 221 set fd [open test.db-journal w] | |
| 222 fconfigure $fd -translation binary | |
| 223 puts -nonewline $fd $zJournal | |
| 224 close $fd | |
| 225 execsql { | |
| 226 SELECT count(*) FROM t1; | |
| 227 PRAGMA integrity_check | |
| 228 } | |
| 229 } {6 ok} | |
| 230 | |
| 231 | |
| 232 # If a connection running in persistent-journal mode is part of a | |
| 233 # multi-file transaction, it must ensure that the master-journal name | |
| 234 # appended to the journal file contents during the commit is located | |
| 235 # at the end of the physical journal file. If there was already a | |
| 236 # large journal file allocated at the start of the transaction, this | |
| 237 # may mean truncating the file so that the master journal name really | |
| 238 # is at the physical end of the file. | |
| 239 # | |
| 240 # This block of tests test that SQLite correctly truncates such | |
| 241 # journal files, and that the results behave correctly if a hot-journal | |
| 242 # rollback occurs. | |
| 243 # | |
| 244 ifcapable pragma { | |
| 245 reset_db | |
| 246 file delete -force test2.db | |
| 247 | |
| 248 do_test crash8-4.1 { | |
| 249 execsql { | |
| 250 PRAGMA journal_mode = persist; | |
| 251 CREATE TABLE ab(a, b); | |
| 252 INSERT INTO ab VALUES(0, 'abc'); | |
| 253 INSERT INTO ab VALUES(1, NULL); | |
| 254 INSERT INTO ab VALUES(2, NULL); | |
| 255 INSERT INTO ab VALUES(3, NULL); | |
| 256 INSERT INTO ab VALUES(4, NULL); | |
| 257 INSERT INTO ab VALUES(5, NULL); | |
| 258 INSERT INTO ab VALUES(6, NULL); | |
| 259 UPDATE ab SET b = randstr(1000,1000); | |
| 260 ATTACH 'test2.db' AS aux; | |
| 261 CREATE TABLE aux.ab(a, b); | |
| 262 INSERT INTO aux.ab SELECT * FROM main.ab; | |
| 263 | |
| 264 UPDATE aux.ab SET b = randstr(1000,1000) WHERE a>=1; | |
| 265 UPDATE ab SET b = randstr(1000,1000) WHERE a>=1; | |
| 266 } | |
| 267 list [file exists test.db-journal] [file exists test2.db-journal] | |
| 268 } {1 1} | |
| 269 | |
| 270 do_test crash8-4.2 { | |
| 271 execsql { | |
| 272 BEGIN; | |
| 273 UPDATE aux.ab SET b = 'def' WHERE a = 0; | |
| 274 UPDATE main.ab SET b = 'def' WHERE a = 0; | |
| 275 COMMIT; | |
| 276 } | |
| 277 } {} | |
| 278 | |
| 279 do_test crash8-4.3 { | |
| 280 execsql { | |
| 281 UPDATE aux.ab SET b = randstr(1000,1000) WHERE a>=1; | |
| 282 UPDATE ab SET b = randstr(1000,1000) WHERE a>=1; | |
| 283 } | |
| 284 } {} | |
| 285 | |
| 286 set contents_main [db eval {SELECT b FROM main.ab WHERE a = 1}] | |
| 287 set contents_aux [db eval {SELECT b FROM aux.ab WHERE a = 1}] | |
| 288 | |
| 289 do_test crash8-4.4 { | |
| 290 crashsql -file test2.db -delay 1 { | |
| 291 ATTACH 'test2.db' AS aux; | |
| 292 BEGIN; | |
| 293 UPDATE aux.ab SET b = 'ghi' WHERE a = 0; | |
| 294 UPDATE main.ab SET b = 'ghi' WHERE a = 0; | |
| 295 COMMIT; | |
| 296 } | |
| 297 } {1 {child process exited abnormally}} | |
| 298 | |
| 299 do_test crash8-4.5 { | |
| 300 list [file exists test.db-journal] [file exists test2.db-journal] | |
| 301 } {1 1} | |
| 302 | |
| 303 do_test crash8-4.6 { | |
| 304 execsql { | |
| 305 SELECT b FROM main.ab WHERE a = 0; | |
| 306 SELECT b FROM aux.ab WHERE a = 0; | |
| 307 } | |
| 308 } {def def} | |
| 309 | |
| 310 do_test crash8-4.7 { | |
| 311 crashsql -file test2.db -delay 1 { | |
| 312 ATTACH 'test2.db' AS aux; | |
| 313 BEGIN; | |
| 314 UPDATE aux.ab SET b = 'jkl' WHERE a = 0; | |
| 315 UPDATE main.ab SET b = 'jkl' WHERE a = 0; | |
| 316 COMMIT; | |
| 317 } | |
| 318 } {1 {child process exited abnormally}} | |
| 319 | |
| 320 do_test crash8-4.8 { | |
| 321 set fd [open test.db-journal] | |
| 322 fconfigure $fd -translation binary | |
| 323 seek $fd -16 end | |
| 324 binary scan [read $fd 4] I len | |
| 325 | |
| 326 seek $fd [expr {-1 * ($len + 16)}] end | |
| 327 set zMasterJournal [read $fd $len] | |
| 328 close $fd | |
| 329 | |
| 330 file exists $zMasterJournal | |
| 331 } {1} | |
| 332 | |
| 333 do_test crash8-4.9 { | |
| 334 execsql { SELECT b FROM aux.ab WHERE a = 0 } | |
| 335 } {def} | |
| 336 | |
| 337 do_test crash8-4.10 { | |
| 338 file delete $zMasterJournal | |
| 339 execsql { SELECT b FROM main.ab WHERE a = 0 } | |
| 340 } {jkl} | |
| 341 } | |
| 342 | |
| 343 finish_test | |
| OLD | NEW |