1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TclScript/ioerr.test Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,409 @@
1.4 +# 2001 October 12
1.5 +#
1.6 +# Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
1.7 +#
1.8 +# The author disclaims copyright to this source code. In place of
1.9 +# a legal notice, here is a blessing:
1.10 +#
1.11 +# May you do good and not evil.
1.12 +# May you find forgiveness for yourself and forgive others.
1.13 +# May you share freely, never taking more than you give.
1.14 +#
1.15 +#***********************************************************************
1.16 +# This file implements regression tests for SQLite library. The
1.17 +# focus of this file is testing for correct handling of I/O errors
1.18 +# such as writes failing because the disk is full.
1.19 +#
1.20 +# The tests in this file use special facilities that are only
1.21 +# available in the SQLite test fixture.
1.22 +#
1.23 +# $Id: ioerr.test,v 1.41 2008/07/12 14:52:20 drh Exp $
1.24 +
1.25 +set testdir [file dirname $argv0]
1.26 +source $testdir/tester.tcl
1.27 +
1.28 +# If SQLITE_DEFAULT_AUTOVACUUM is set to true, then a simulated IO error
1.29 +# on the 8th IO operation in the SQL script below doesn't report an error.
1.30 +#
1.31 +# This is because the 8th IO call attempts to read page 2 of the database
1.32 +# file when the file on disk is only 1 page. The pager layer detects that
1.33 +# this has happened and suppresses the error returned by the OS layer.
1.34 +#
1.35 +do_ioerr_test ioerr-1 -erc 1 -ckrefcount 1 -sqlprep {
1.36 + SELECT * FROM sqlite_master;
1.37 +} -sqlbody {
1.38 + CREATE TABLE t1(a,b,c);
1.39 + SELECT * FROM sqlite_master;
1.40 + BEGIN TRANSACTION;
1.41 + INSERT INTO t1 VALUES(1,2,3);
1.42 + INSERT INTO t1 VALUES(4,5,6);
1.43 + ROLLBACK;
1.44 + SELECT * FROM t1;
1.45 + BEGIN TRANSACTION;
1.46 + INSERT INTO t1 VALUES(1,2,3);
1.47 + INSERT INTO t1 VALUES(4,5,6);
1.48 + COMMIT;
1.49 + SELECT * FROM t1;
1.50 + DELETE FROM t1 WHERE a<100;
1.51 +} -exclude [expr [string match [execsql {pragma auto_vacuum}] 1] ? 4 : 0]
1.52 +
1.53 +# Test for IO errors during a VACUUM.
1.54 +#
1.55 +# The first IO call is excluded from the test. This call attempts to read
1.56 +# the file-header of the temporary database used by VACUUM. Since the
1.57 +# database doesn't exist at that point, the IO error is not detected.
1.58 +#
1.59 +# Additionally, if auto-vacuum is enabled, the 12th IO error is not
1.60 +# detected. Same reason as the 8th in the test case above.
1.61 +#
1.62 +ifcapable vacuum {
1.63 + do_ioerr_test ioerr-2 -cksum true -ckrefcount true -sqlprep {
1.64 + BEGIN;
1.65 + CREATE TABLE t1(a, b, c);
1.66 + INSERT INTO t1 VALUES(1, randstr(50,50), randstr(50,50));
1.67 + INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
1.68 + INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
1.69 + INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
1.70 + INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
1.71 + INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
1.72 + INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
1.73 + INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
1.74 + INSERT INTO t1 VALUES(1, randstr(600,600), randstr(600,600));
1.75 + CREATE TABLE t2 AS SELECT * FROM t1;
1.76 + CREATE TABLE t3 AS SELECT * FROM t1;
1.77 + COMMIT;
1.78 + DROP TABLE t2;
1.79 + } -sqlbody {
1.80 + VACUUM;
1.81 + } -exclude [list \
1.82 + 1 [expr [string match [execsql {pragma auto_vacuum}] 1]?9:-1]]
1.83 +}
1.84 +
1.85 +do_ioerr_test ioerr-3 -ckrefcount true -tclprep {
1.86 + execsql {
1.87 + PRAGMA cache_size = 10;
1.88 + BEGIN;
1.89 + CREATE TABLE abc(a);
1.90 + INSERT INTO abc VALUES(randstr(1500,1500)); -- Page 4 is overflow
1.91 + }
1.92 + for {set i 0} {$i<150} {incr i} {
1.93 + execsql {
1.94 + INSERT INTO abc VALUES(randstr(100,100));
1.95 + }
1.96 + }
1.97 + execsql COMMIT
1.98 +} -sqlbody {
1.99 + CREATE TABLE abc2(a);
1.100 + BEGIN;
1.101 + DELETE FROM abc WHERE length(a)>100;
1.102 + UPDATE abc SET a = randstr(90,90);
1.103 + COMMIT;
1.104 + CREATE TABLE abc3(a);
1.105 +}
1.106 +
1.107 +# Test IO errors that can occur retrieving a record header that flows over
1.108 +# onto an overflow page.
1.109 +do_ioerr_test ioerr-4 -ckrefcount true -tclprep {
1.110 + set sql "CREATE TABLE abc(a1"
1.111 + for {set i 2} {$i<1300} {incr i} {
1.112 + append sql ", a$i"
1.113 + }
1.114 + append sql ");"
1.115 + execsql $sql
1.116 + execsql {INSERT INTO abc (a1) VALUES(NULL)}
1.117 +} -sqlbody {
1.118 + SELECT * FROM abc;
1.119 +}
1.120 +
1.121 +
1.122 +# Test IO errors that may occur during a multi-file commit.
1.123 +#
1.124 +# Tests 8 and 17 are excluded when auto-vacuum is enabled for the same
1.125 +# reason as in test cases ioerr-1.XXX
1.126 +ifcapable attach {
1.127 + set ex ""
1.128 + if {[string match [execsql {pragma auto_vacuum}] 1]} {
1.129 + set ex [list 4 17]
1.130 + }
1.131 + do_ioerr_test ioerr-5 -restoreprng 0 -ckrefcount true -sqlprep {
1.132 + ATTACH 'test2.db' AS test2;
1.133 + } -sqlbody {
1.134 + BEGIN;
1.135 + CREATE TABLE t1(a,b,c);
1.136 + CREATE TABLE test2.t2(a,b,c);
1.137 + COMMIT;
1.138 + } -exclude $ex
1.139 +}
1.140 +
1.141 +# Test IO errors when replaying two hot journals from a 2-file
1.142 +# transaction. This test only runs on UNIX.
1.143 +ifcapable crashtest&&attach {
1.144 + if {![catch {sqlite3 -has_codec} r] && !$r} {
1.145 + do_ioerr_test ioerr-6 -ckrefcount true -tclprep {
1.146 + execsql {
1.147 + ATTACH 'test2.db' as aux;
1.148 + CREATE TABLE tx(a, b);
1.149 + CREATE TABLE aux.ty(a, b);
1.150 + }
1.151 + set rc [crashsql -delay 2 -file test2.db-journal {
1.152 + ATTACH 'test2.db' as aux;
1.153 + PRAGMA cache_size = 10;
1.154 + BEGIN;
1.155 + CREATE TABLE aux.t2(a, b, c);
1.156 + CREATE TABLE t1(a, b, c);
1.157 + COMMIT;
1.158 + }]
1.159 + if {$rc!="1 {child process exited abnormally}"} {
1.160 + error "Wrong error message: $rc"
1.161 + }
1.162 + } -sqlbody {
1.163 + SELECT * FROM sqlite_master;
1.164 + SELECT * FROM aux.sqlite_master;
1.165 + }
1.166 + }
1.167 +}
1.168 +
1.169 +# Test handling of IO errors that occur while rolling back hot journal
1.170 +# files.
1.171 +#
1.172 +# These tests can't be run on windows because the windows version of
1.173 +# SQLite holds a mandatory exclusive lock on journal files it has open.
1.174 +#
1.175 +if {$tcl_platform(platform)!="windows" && $tcl_platform(platform)!="symbian"} {
1.176 + do_ioerr_test ioerr-7 -tclprep {
1.177 + db close
1.178 + sqlite3 db2 test2.db
1.179 + db2 eval {
1.180 + PRAGMA synchronous = 0;
1.181 + CREATE TABLE t1(a, b);
1.182 + INSERT INTO t1 VALUES(1, 2);
1.183 + BEGIN;
1.184 + INSERT INTO t1 VALUES(3, 4);
1.185 + }
1.186 + copy_file test2.db test.db
1.187 + copy_file test2.db-journal test.db-journal
1.188 + db2 close
1.189 + } -tclbody {
1.190 + sqlite3 db test.db
1.191 + db eval {
1.192 + SELECT * FROM t1;
1.193 + }
1.194 + } -exclude 1
1.195 +}
1.196 +
1.197 +# For test coverage: Cause an I/O failure while trying to read a
1.198 +# short field (one that fits into a Mem buffer without mallocing
1.199 +# for space).
1.200 +#
1.201 +do_ioerr_test ioerr-8 -ckrefcount true -tclprep {
1.202 + execsql {
1.203 + CREATE TABLE t1(a,b,c);
1.204 + INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
1.205 + }
1.206 + db close
1.207 + sqlite3 db test.db
1.208 +} -sqlbody {
1.209 + SELECT c FROM t1;
1.210 +}
1.211 +
1.212 +# For test coverage: Cause an IO error whilst reading the master-journal
1.213 +# name from a journal file.
1.214 +if {$tcl_platform(platform)=="unix"} {
1.215 + do_ioerr_test ioerr-9 -ckrefcount true -tclprep {
1.216 + execsql {
1.217 + CREATE TABLE t1(a,b,c);
1.218 + INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
1.219 + BEGIN;
1.220 + INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
1.221 + }
1.222 + copy_file test.db-journal test2.db-journal
1.223 + execsql {
1.224 + COMMIT;
1.225 + }
1.226 + copy_file test2.db-journal test.db-journal
1.227 + set f [open test.db-journal a]
1.228 + fconfigure $f -encoding binary
1.229 + puts -nonewline $f "hello"
1.230 + puts -nonewline $f "\x00\x00\x00\x05\x01\x02\x03\x04"
1.231 + puts -nonewline $f "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
1.232 + close $f
1.233 + } -sqlbody {
1.234 + SELECT a FROM t1;
1.235 + }
1.236 +}
1.237 +
1.238 +# For test coverage: Cause an IO error during statement playback (i.e.
1.239 +# a constraint).
1.240 +do_ioerr_test ioerr-10 -ckrefcount true -tclprep {
1.241 + execsql {
1.242 + BEGIN;
1.243 + CREATE TABLE t1(a PRIMARY KEY, b);
1.244 + }
1.245 + for {set i 0} {$i < 500} {incr i} {
1.246 + execsql {INSERT INTO t1 VALUES(:i, 'hello world');}
1.247 + }
1.248 + execsql {
1.249 + COMMIT;
1.250 + }
1.251 +} -tclbody {
1.252 +
1.253 + catch {execsql {
1.254 + BEGIN;
1.255 + INSERT INTO t1 VALUES('abc', 123);
1.256 + INSERT INTO t1 VALUES('def', 123);
1.257 + INSERT INTO t1 VALUES('ghi', 123);
1.258 + INSERT INTO t1 SELECT (a+500)%900, 'good string' FROM t1;
1.259 + }} msg
1.260 +
1.261 + if {$msg != "column a is not unique"} {
1.262 + error $msg
1.263 + }
1.264 +}
1.265 +
1.266 +# Assertion fault bug reported by alex dimitrov.
1.267 +#
1.268 +do_ioerr_test ioerr-11 -ckrefcount true -erc 1 -sqlprep {
1.269 + CREATE TABLE A(Id INTEGER, Name TEXT);
1.270 + INSERT INTO A(Id, Name) VALUES(1, 'Name');
1.271 +} -sqlbody {
1.272 + UPDATE A SET Id = 2, Name = 'Name2' WHERE Id = 1;
1.273 +}
1.274 +
1.275 +# Test that an io error encountered in a sync() caused by a call to
1.276 +# sqlite3_release_memory() is handled Ok. Only try this if
1.277 +# memory-management is enabled.
1.278 +#
1.279 +ifcapable memorymanage {
1.280 + do_ioerr_test memmanage-ioerr1 -ckrefcount true -sqlprep {
1.281 + BEGIN;
1.282 + CREATE TABLE t1(a, b, c);
1.283 + INSERT INTO t1 VALUES(randstr(50,50), randstr(100,100), randstr(10,10));
1.284 + INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
1.285 + INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
1.286 + INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
1.287 + INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
1.288 + INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
1.289 + } -tclbody {
1.290 + sqlite3_release_memory
1.291 + } -sqlbody {
1.292 + COMMIT;
1.293 + }
1.294 +}
1.295 +
1.296 +ifcapable pager_pragmas&&autovacuum {
1.297 + do_ioerr_test ioerr-12 -ckrefcount true -erc 1 -sqlprep {
1.298 + PRAGMA page_size = 512;
1.299 + PRAGMA auto_vacuum = incremental;
1.300 + CREATE TABLE t1(x);
1.301 + INSERT INTO t1 VALUES( randomblob(1 * (512-4)) );
1.302 + INSERT INTO t1 VALUES( randomblob(110 * (512-4)) );
1.303 + INSERT INTO t1 VALUES( randomblob(2 * (512-4)) );
1.304 + INSERT INTO t1 VALUES( randomblob(110 * (512-4)) );
1.305 + INSERT INTO t1 VALUES( randomblob(3 * (512-4)) );
1.306 + DELETE FROM t1 WHERE rowid = 3;
1.307 + PRAGMA incremental_vacuum = 2;
1.308 + DELETE FROM t1 WHERE rowid = 1;
1.309 + } -sqlbody {
1.310 + PRAGMA incremental_vacuum = 1;
1.311 + }
1.312 +}
1.313 +
1.314 +# Usually, after a new page is allocated from the end of the file, it does
1.315 +# not need to be written to the journal. The exception is when the new page
1.316 +# shares its sector with an existing page that does need to be journalled.
1.317 +# This test case provokes this condition to test for the sake of coverage
1.318 +# that an IO error while journalling the coresident page is handled correctly.
1.319 +#
1.320 +sqlite3_simulate_device -char {} -sectorsize 2048
1.321 +do_ioerr_test ioerr-12 -ckrefcount true -erc 1 -tclprep {
1.322 + db close
1.323 + sqlite3 db test.db -vfs devsym
1.324 +
1.325 + # Create a test database. Page 2 is the root page of table t1. The only
1.326 + # row inserted into t1 has an overflow page - page 3. Page 3 will be
1.327 + # coresident on the 2048 byte sector with the next page to be allocated.
1.328 + #
1.329 + db eval { PRAGMA page_size = 1024 }
1.330 + db eval { CREATE TABLE t1(x) }
1.331 + db eval { INSERT INTO t1 VALUES(randomblob(1100)); }
1.332 +} -tclbody {
1.333 + db eval { INSERT INTO t1 VALUES(randomblob(2000)); }
1.334 +}
1.335 +sqlite3_simulate_device -char {} -sectorsize 0
1.336 +catch {db close}
1.337 +
1.338 +do_ioerr_test ioerr-13 -ckrefcount true -erc 1 -sqlprep {
1.339 + PRAGMA auto_vacuum = incremental;
1.340 + CREATE TABLE t1(x);
1.341 + CREATE TABLE t2(x);
1.342 + INSERT INTO t2 VALUES(randomblob(1500));
1.343 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.344 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.345 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.346 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.347 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.348 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.349 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.350 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.351 + INSERT INTO t1 VALUES(randomblob(20));
1.352 + INSERT INTO t1 SELECT x FROM t1;
1.353 + INSERT INTO t1 SELECT x FROM t1;
1.354 + INSERT INTO t1 SELECT x FROM t1;
1.355 + INSERT INTO t1 SELECT x FROM t1;
1.356 + INSERT INTO t1 SELECT x FROM t1;
1.357 + INSERT INTO t1 SELECT x FROM t1; /* 64 entries in t1 */
1.358 + INSERT INTO t1 SELECT x FROM t1 LIMIT 14; /* 78 entries in t1 */
1.359 + DELETE FROM t2 WHERE rowid = 3;
1.360 +} -sqlbody {
1.361 + -- This statement uses the balance_quick() optimization. The new page
1.362 + -- is appended to the database file. But the overflow page used by
1.363 + -- the new record will be positioned near the start of the database
1.364 + -- file, in the gap left by the "DELETE FROM t2 WHERE rowid=3" statement
1.365 + -- above.
1.366 + --
1.367 + -- The point of this is that the statement wil need to update two pointer
1.368 + -- map pages. Which introduces another opportunity for an IO error.
1.369 + --
1.370 + INSERT INTO t1 VALUES(randomblob(2000));
1.371 +}
1.372 +
1.373 +do_ioerr_test ioerr-14 -ckrefcount true -erc 1 -sqlprep {
1.374 + PRAGMA auto_vacuum = incremental;
1.375 + CREATE TABLE t1(x);
1.376 + CREATE TABLE t2(x);
1.377 + INSERT INTO t2 VALUES(randomblob(1500));
1.378 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.379 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.380 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.381 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.382 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.383 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.384 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.385 + INSERT INTO t2 SELECT randomblob(1500) FROM t2;
1.386 +
1.387 + -- This statement inserts a row into t1 with an overflow page at the
1.388 + -- end of the file. A long way from its parent (the root of t1).
1.389 + INSERT INTO t1 VALUES(randomblob(1500));
1.390 + DELETE FROM t2 WHERE rowid<10;
1.391 +} -sqlbody {
1.392 + -- This transaction will cause the root-page of table t1 to divide
1.393 + -- (by calling balance_deeper()). When it does, the "parent" page of the
1.394 + -- overflow page inserted in the -sqlprep block above will change and
1.395 + -- the corresponding pointer map page be updated. This test case attempts
1.396 + -- to cause an IO error during the pointer map page update.
1.397 + --
1.398 + BEGIN;
1.399 + INSERT INTO t1 VALUES(randomblob(100));
1.400 + INSERT INTO t1 VALUES(randomblob(100));
1.401 + INSERT INTO t1 VALUES(randomblob(100));
1.402 + INSERT INTO t1 VALUES(randomblob(100));
1.403 + INSERT INTO t1 VALUES(randomblob(100));
1.404 + INSERT INTO t1 VALUES(randomblob(100));
1.405 + INSERT INTO t1 VALUES(randomblob(100));
1.406 + INSERT INTO t1 VALUES(randomblob(100));
1.407 + INSERT INTO t1 VALUES(randomblob(100));
1.408 + INSERT INTO t1 VALUES(randomblob(100));
1.409 + COMMIT;
1.410 +}
1.411 +
1.412 +finish_test