1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TclScript/crash.test Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,411 @@
1.4 +# 2001 September 15
1.5 +#
1.6 +# The author disclaims copyright to this source code. In place of
1.7 +# a legal notice, here is a blessing:
1.8 +#
1.9 +# May you do good and not evil.
1.10 +# May you find forgiveness for yourself and forgive others.
1.11 +# May you share freely, never taking more than you give.
1.12 +#
1.13 +#***********************************************************************
1.14 +# This file implements regression tests for SQLite library.
1.15 +#
1.16 +# The focus of this file is testing the ability of the database to
1.17 +# uses its rollback journal to recover intact (no database corruption)
1.18 +# from a power failure during the middle of a COMMIT. The OS interface
1.19 +# modules are overloaded using the modified I/O routines found in test6.c.
1.20 +# These routines allow us to simulate the kind of file damage that
1.21 +# occurs after a power failure.
1.22 +#
1.23 +# $Id: crash.test,v 1.27 2008/01/08 15:18:52 drh Exp $
1.24 +
1.25 +set testdir [file dirname $argv0]
1.26 +source $testdir/tester.tcl
1.27 +
1.28 +ifcapable !crashtest {
1.29 + finish_test
1.30 + return
1.31 +}
1.32 +
1.33 +set repeats 100
1.34 +#set repeats 10
1.35 +
1.36 +# The following procedure computes a "signature" for table "abc". If
1.37 +# abc changes in any way, the signature should change.
1.38 +proc signature {} {
1.39 + return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
1.40 +}
1.41 +proc signature2 {} {
1.42 + return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc2}]
1.43 +}
1.44 +
1.45 +#--------------------------------------------------------------------------
1.46 +# Simple crash test:
1.47 +#
1.48 +# crash-1.1: Create a database with a table with two rows.
1.49 +# crash-1.2: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
1.50 +# the first journal-sync.
1.51 +# crash-1.3: Ensure the database is in the same state as after crash-1.1.
1.52 +# crash-1.4: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
1.53 +# the first database-sync.
1.54 +# crash-1.5: Ensure the database is in the same state as after crash-1.1.
1.55 +# crash-1.6: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
1.56 +# the second journal-sync.
1.57 +# crash-1.7: Ensure the database is in the same state as after crash-1.1.
1.58 +#
1.59 +# Tests 1.8 through 1.11 test for crashes on the third journal sync and
1.60 +# second database sync. Neither of these is required in such a small test
1.61 +# case, so these tests are just to verify that the test infrastructure
1.62 +# operates as expected.
1.63 +#
1.64 +do_test crash-1.1 {
1.65 + execsql {
1.66 + CREATE TABLE abc(a, b, c);
1.67 + INSERT INTO abc VALUES(1, 2, 3);
1.68 + INSERT INTO abc VALUES(4, 5, 6);
1.69 + }
1.70 + set ::sig [signature]
1.71 + expr 0
1.72 +} {0}
1.73 +for {set i 0} {$i<10} {incr i} {
1.74 + set seed [expr {int(abs(rand()*10000))}]
1.75 + do_test crash-1.2.$i {
1.76 + crashsql -delay 1 -file test.db-journal -seed $seed {
1.77 + DELETE FROM abc WHERE a = 1;
1.78 + }
1.79 + } {1 {child process exited abnormally}}
1.80 + do_test crash-1.3.$i {
1.81 + signature
1.82 + } $::sig
1.83 +}
1.84 +do_test crash-1.4 {
1.85 + crashsql -delay 1 -file test.db {
1.86 + DELETE FROM abc WHERE a = 1;
1.87 + }
1.88 +} {1 {child process exited abnormally}}
1.89 +do_test crash-1.5 {
1.90 + signature
1.91 +} $::sig
1.92 +do_test crash-1.6 {
1.93 + crashsql -delay 2 -file test.db-journal {
1.94 + DELETE FROM abc WHERE a = 1;
1.95 + }
1.96 +} {1 {child process exited abnormally}}
1.97 +do_test crash-1.7 {
1.98 + catchsql {
1.99 + SELECT * FROM abc;
1.100 + }
1.101 +} {0 {1 2 3 4 5 6}}
1.102 +
1.103 +do_test crash-1.8 {
1.104 + crashsql -delay 3 -file test.db-journal {
1.105 + DELETE FROM abc WHERE a = 1;
1.106 + }
1.107 +} {0 {}}
1.108 +do_test crash-1.9 {
1.109 + catchsql {
1.110 + SELECT * FROM abc;
1.111 + }
1.112 +} {0 {4 5 6}}
1.113 +do_test crash-1.10 {
1.114 + crashsql -delay 2 -file test.db {
1.115 + DELETE FROM abc WHERE a = 4;
1.116 + }
1.117 +} {0 {}}
1.118 +do_test crash-1.11 {
1.119 + catchsql {
1.120 + SELECT * FROM abc;
1.121 + }
1.122 +} {0 {}}
1.123 +
1.124 +#--------------------------------------------------------------------------
1.125 +# The following tests test recovery when both the database file and the the
1.126 +# journal file contain corrupt data. This can happen after pages are
1.127 +# written to the database file before a transaction is committed due to
1.128 +# cache-pressure.
1.129 +#
1.130 +# crash-2.1: Insert 18 pages of data into the database.
1.131 +# crash-2.2: Check the database file size looks ok.
1.132 +# crash-2.3: Delete 15 or so pages (with a 10 page page-cache), then crash.
1.133 +# crash-2.4: Ensure the database is in the same state as after crash-2.1.
1.134 +#
1.135 +# Test cases crash-2.5 and crash-2.6 check that the database is OK if the
1.136 +# crash occurs during the main database file sync. But this isn't really
1.137 +# different from the crash-1.* cases.
1.138 +#
1.139 +do_test crash-2.1 {
1.140 + execsql { BEGIN }
1.141 + for {set n 0} {$n < 1000} {incr n} {
1.142 + execsql "INSERT INTO abc VALUES($n, [expr 2*$n], [expr 3*$n])"
1.143 + }
1.144 + execsql { COMMIT }
1.145 + set ::sig [signature]
1.146 + execsql { SELECT sum(a), sum(b), sum(c) from abc }
1.147 +} {499500 999000 1498500}
1.148 +do_test crash-2.2 {
1.149 + expr ([file size test.db] / 1024)>16
1.150 +} {1}
1.151 +do_test crash-2.3 {
1.152 + crashsql -delay 2 -file test.db-journal {
1.153 + DELETE FROM abc WHERE a < 800;
1.154 + }
1.155 +} {1 {child process exited abnormally}}
1.156 +do_test crash-2.4 {
1.157 + signature
1.158 +} $sig
1.159 +do_test crash-2.5 {
1.160 + crashsql -delay 1 -file test.db {
1.161 + DELETE FROM abc WHERE a<800;
1.162 + }
1.163 +} {1 {child process exited abnormally}}
1.164 +do_test crash-2.6 {
1.165 + signature
1.166 +} $sig
1.167 +
1.168 +#--------------------------------------------------------------------------
1.169 +# The crash-3.* test cases are essentially the same test as test case
1.170 +# crash-2.*, but with a more complicated data set.
1.171 +#
1.172 +# The test is repeated a few times with different seeds for the random
1.173 +# number generator in the crashing executable. Because there is no way to
1.174 +# seed the random number generator directly, some SQL is added to the test
1.175 +# case to 'use up' a different quantity random numbers before the test SQL
1.176 +# is executed.
1.177 +#
1.178 +
1.179 +# Make sure the file is much bigger than the pager-cache (10 pages). This
1.180 +# ensures that cache-spills happen regularly.
1.181 +do_test crash-3.0 {
1.182 + execsql {
1.183 + INSERT INTO abc SELECT * FROM abc;
1.184 + INSERT INTO abc SELECT * FROM abc;
1.185 + INSERT INTO abc SELECT * FROM abc;
1.186 + INSERT INTO abc SELECT * FROM abc;
1.187 + INSERT INTO abc SELECT * FROM abc;
1.188 + }
1.189 + expr ([file size test.db] / 1024) > 450
1.190 +} {1}
1.191 +for {set i 1} {$i < $repeats} {incr i} {
1.192 + set sig [signature]
1.193 + do_test crash-3.$i.1 {
1.194 + set seed [expr {int(abs(rand()*10000))}]
1.195 + crashsql -delay [expr $i%5 + 1] -file test.db-journal -seed $seed "
1.196 + BEGIN;
1.197 + SELECT random() FROM abc LIMIT $i;
1.198 + INSERT INTO abc VALUES(randstr(10,10), 0, 0);
1.199 + DELETE FROM abc WHERE random()%10!=0;
1.200 + COMMIT;
1.201 + "
1.202 + } {1 {child process exited abnormally}}
1.203 + do_test crash-3.$i.2 {
1.204 + signature
1.205 + } $sig
1.206 +}
1.207 +
1.208 +#--------------------------------------------------------------------------
1.209 +# The following test cases - crash-4.* - test the correct recovery of the
1.210 +# database when a crash occurs during a multi-file transaction.
1.211 +#
1.212 +# crash-4.1.*: Test recovery when crash occurs during sync() of the
1.213 +# main database journal file.
1.214 +# crash-4.2.*: Test recovery when crash occurs during sync() of an
1.215 +# attached database journal file.
1.216 +# crash-4.3.*: Test recovery when crash occurs during sync() of the master
1.217 +# journal file.
1.218 +#
1.219 +ifcapable attach {
1.220 + do_test crash-4.0 {
1.221 + file delete -force test2.db
1.222 + file delete -force test2.db-journal
1.223 + execsql {
1.224 + ATTACH 'test2.db' AS aux;
1.225 + PRAGMA aux.default_cache_size = 10;
1.226 + CREATE TABLE aux.abc2 AS SELECT 2*a as a, 2*b as b, 2*c as c FROM abc;
1.227 + }
1.228 + expr ([file size test2.db] / 1024) > 450
1.229 + } {1}
1.230 +
1.231 + set fin 0
1.232 + for {set i 1} {$i<$repeats} {incr i} {
1.233 + set seed [expr {int(abs(rand()*10000))}]
1.234 + set sig [signature]
1.235 + set sig2 [signature2]
1.236 + do_test crash-4.1.$i.1 {
1.237 + set c [crashsql -delay $i -file test.db-journal -seed $::seed "
1.238 + ATTACH 'test2.db' AS aux;
1.239 + BEGIN;
1.240 + SELECT randstr($i,$i) FROM abc LIMIT $i;
1.241 + INSERT INTO abc VALUES(randstr(10,10), 0, 0);
1.242 + DELETE FROM abc WHERE random()%10!=0;
1.243 + INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
1.244 + DELETE FROM abc2 WHERE random()%10!=0;
1.245 + COMMIT;
1.246 + "]
1.247 + if { $c == {0 {}} } {
1.248 + set ::fin 1
1.249 + set c {1 {child process exited abnormally}}
1.250 + }
1.251 + set c
1.252 + } {1 {child process exited abnormally}}
1.253 + if {$::fin} break
1.254 + do_test crash-4.1.$i.2 {
1.255 + signature
1.256 + } $sig
1.257 + do_test crash-4.1.$i.3 {
1.258 + signature2
1.259 + } $sig2
1.260 + }
1.261 + set i 0
1.262 + set fin 0
1.263 + while {[incr i]} {
1.264 + set seed [expr {int(abs(rand()*10000))}]
1.265 + set sig [signature]
1.266 + set sig2 [signature2]
1.267 + set ::fin 0
1.268 + do_test crash-4.2.$i.1 {
1.269 + set c [crashsql -delay $i -file test2.db-journal -seed $::seed "
1.270 + ATTACH 'test2.db' AS aux;
1.271 + BEGIN;
1.272 + SELECT randstr($i,$i) FROM abc LIMIT $i;
1.273 + INSERT INTO abc VALUES(randstr(10,10), 0, 0);
1.274 + DELETE FROM abc WHERE random()%10!=0;
1.275 + INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
1.276 + DELETE FROM abc2 WHERE random()%10!=0;
1.277 + COMMIT;
1.278 + "]
1.279 + if { $c == {0 {}} } {
1.280 + set ::fin 1
1.281 + set c {1 {child process exited abnormally}}
1.282 + }
1.283 + set c
1.284 + } {1 {child process exited abnormally}}
1.285 + if { $::fin } break
1.286 + do_test crash-4.2.$i.2 {
1.287 + signature
1.288 + } $sig
1.289 + do_test crash-4.2.$i.3 {
1.290 + signature2
1.291 + } $sig2
1.292 + }
1.293 + for {set i 1} {$i < 5} {incr i} {
1.294 + set sig [signature]
1.295 + set sig2 [signature2]
1.296 + do_test crash-4.3.$i.1 {
1.297 + crashsql -delay 1 -file test.db-mj* "
1.298 + ATTACH 'test2.db' AS aux;
1.299 + BEGIN;
1.300 + SELECT random() FROM abc LIMIT $i;
1.301 + INSERT INTO abc VALUES(randstr(10,10), 0, 0);
1.302 + DELETE FROM abc WHERE random()%10!=0;
1.303 + INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
1.304 + DELETE FROM abc2 WHERE random()%10!=0;
1.305 + COMMIT;
1.306 + "
1.307 + } {1 {child process exited abnormally}}
1.308 + do_test crash-4.3.$i.2 {
1.309 + signature
1.310 + } $sig
1.311 + do_test crash-4.3.$i.3 {
1.312 + signature2
1.313 + } $sig2
1.314 + }
1.315 +}
1.316 +
1.317 +#--------------------------------------------------------------------------
1.318 +# The following test cases - crash-5.* - exposes a bug that existed in the
1.319 +# sqlite3pager_movepage() API used by auto-vacuum databases.
1.320 +# database when a crash occurs during a multi-file transaction. See comments
1.321 +# in test crash-5.3 for details.
1.322 +#
1.323 +db close
1.324 +file delete -force test.db
1.325 +sqlite3 db test.db
1.326 +do_test crash-5.1 {
1.327 + execsql {
1.328 + CREATE TABLE abc(a, b, c); -- Root page 3
1.329 + INSERT INTO abc VALUES(randstr(1500,1500), 0, 0); -- Overflow page 4
1.330 + INSERT INTO abc SELECT * FROM abc;
1.331 + INSERT INTO abc SELECT * FROM abc;
1.332 + INSERT INTO abc SELECT * FROM abc;
1.333 + }
1.334 +} {}
1.335 +do_test crash-5.2 {
1.336 + expr [file size test.db] / 1024
1.337 +} [expr [string match [execsql {pragma auto_vacuum}] 1] ? 11 : 10]
1.338 +set sig [signature]
1.339 +do_test crash-5.3 {
1.340 +# The SQL below is used to expose a bug that existed in
1.341 +# sqlite3pager_movepage() during development of the auto-vacuum feature. It
1.342 +# functions as follows:
1.343 +#
1.344 +# 1: Begin a transaction.
1.345 +# 2: Put page 4 on the free-list (was the overflow page for the row deleted).
1.346 +# 3: Write data to page 4 (it becomes the overflow page for the row inserted).
1.347 +# The old page 4 data has been written to the journal file, but the
1.348 +# journal file has not been sync()hronized.
1.349 +# 4: Create a table, which calls sqlite3pager_movepage() to move page 4
1.350 +# to the end of the database (page 12) to make room for the new root-page.
1.351 +# 5: Put pressure on the pager-cache. This results in page 4 being written
1.352 +# to the database file to make space in the cache to load a new page. The
1.353 +# bug was that page 4 was written to the database file before the journal
1.354 +# is sync()hronized.
1.355 +# 6: Commit. A crash occurs during the sync of the journal file.
1.356 +#
1.357 +# End result: Before the bug was fixed, data has been written to page 4 of the
1.358 +# database file and the journal file does not contain trustworthy rollback
1.359 +# data for this page.
1.360 +#
1.361 + crashsql -delay 1 -file test.db-journal {
1.362 + BEGIN; -- 1
1.363 + DELETE FROM abc WHERE oid = 1; -- 2
1.364 + INSERT INTO abc VALUES(randstr(1500,1500), 0, 0); -- 3
1.365 + CREATE TABLE abc2(a, b, c); -- 4
1.366 + SELECT * FROM abc; -- 5
1.367 + COMMIT; -- 6
1.368 + }
1.369 +} {1 {child process exited abnormally}}
1.370 +integrity_check crash-5.4
1.371 +do_test crash-5.5 {
1.372 + signature
1.373 +} $sig
1.374 +
1.375 +#--------------------------------------------------------------------------
1.376 +# The following test cases - crash-6.* - test that a DROP TABLE operation
1.377 +# is correctly rolled back in the event of a crash while the database file
1.378 +# is being written. This is mainly to test that all pages are written to the
1.379 +# journal file before truncation in an auto-vacuum database.
1.380 +#
1.381 +do_test crash-6.1 {
1.382 + crashsql -delay 1 -file test.db {
1.383 + DROP TABLE abc;
1.384 + }
1.385 +} {1 {child process exited abnormally}}
1.386 +do_test crash-6.2 {
1.387 + signature
1.388 +} $sig
1.389 +
1.390 +#--------------------------------------------------------------------------
1.391 +# These test cases test the case where the master journal file name is
1.392 +# corrupted slightly so that the corruption has to be detected by the
1.393 +# checksum.
1.394 +do_test crash-7.1 {
1.395 + crashsql -delay 1 -file test.db {
1.396 + ATTACH 'test2.db' AS aux;
1.397 + BEGIN;
1.398 + INSERT INTO abc VALUES(randstr(1500,1500), 0, 0);
1.399 + INSERT INTO abc2 VALUES(randstr(1500,1500), 0, 0);
1.400 + COMMIT;
1.401 + }
1.402 +
1.403 + # Change the checksum value for the master journal name.
1.404 + set f [open test.db-journal a]
1.405 + fconfigure $f -encoding binary
1.406 + seek $f [expr [file size test.db-journal] - 12]
1.407 + puts -nonewline $f "\00\00\00\00"
1.408 + close $f
1.409 +} {}
1.410 +do_test crash-7.2 {
1.411 + signature
1.412 +} $sig
1.413 +
1.414 +finish_test