os/persistentdata/persistentstorage/sqlite3api/TEST/TclScript/ioerr.test
changeset 0 bde4ae8d615e
     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