1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TclScript/ioerr5.test Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,221 @@
1.4 +# 2008 May 12
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 +#
1.15 +# This file tests that if sqlite3_release_memory() is called to reclaim
1.16 +# memory from a pager that is in the error-state, SQLite does not
1.17 +# incorrectly write dirty pages out to the database (not safe to do
1.18 +# once the pager is in error state).
1.19 +#
1.20 +# $Id: ioerr5.test,v 1.5 2008/08/28 18:35:34 danielk1977 Exp $
1.21 +
1.22 +set testdir [file dirname $argv0]
1.23 +source $testdir/tester.tcl
1.24 +
1.25 +ifcapable !memorymanage||!shared_cache {
1.26 + finish_test
1.27 + return
1.28 +}
1.29 +
1.30 +db close
1.31 +
1.32 +set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
1.33 +set ::soft_limit [sqlite3_soft_heap_limit 1048576]
1.34 +
1.35 +# This procedure prepares, steps and finalizes an SQL statement via the
1.36 +# UTF-16 APIs. The text representation of an SQLite error code is returned
1.37 +# ("SQLITE_OK", "SQLITE_IOERR" etc.). The actual results returned by the
1.38 +# SQL statement, if it is a SELECT, are not available.
1.39 +#
1.40 +# This can be useful for testing because it forces SQLite to make an extra
1.41 +# call to sqlite3_malloc() when translating from the supplied UTF-16 to
1.42 +# the UTF-8 encoding used internally.
1.43 +#
1.44 +proc dosql16 {zSql {db db}} {
1.45 + set sql [encoding convertto unicode $zSql]
1.46 + append sql "\00\00"
1.47 + set stmt [sqlite3_prepare16 $db $sql -1 {}]
1.48 + sqlite3_step $stmt
1.49 + set rc [sqlite3_finalize $stmt]
1.50 +}
1.51 +
1.52 +proc compilesql16 {zSql {db db}} {
1.53 + set sql [encoding convertto unicode $zSql]
1.54 + append sql "\00\00"
1.55 + set stmt [sqlite3_prepare16 $db $sql -1 {}]
1.56 + set rc [sqlite3_finalize $stmt]
1.57 +}
1.58 +
1.59 +# Open two database connections (handle db and db2) to database "test.db".
1.60 +#
1.61 +proc opendatabases {} {
1.62 + catch {db close}
1.63 + catch {db2 close}
1.64 + sqlite3 db test.db
1.65 + sqlite3 db2 test.db
1.66 + db2 cache size 0
1.67 + db cache size 0
1.68 + execsql {
1.69 + pragma page_size=512;
1.70 + pragma auto_vacuum=2;
1.71 + pragma cache_size=16;
1.72 + }
1.73 +}
1.74 +
1.75 +# Open two database connections and create a single table in the db.
1.76 +#
1.77 +do_test ioerr5-1.0 {
1.78 + opendatabases
1.79 + execsql { CREATE TABLE A(Id INTEGER, Name TEXT) }
1.80 +} {}
1.81 +
1.82 +foreach locking_mode {normal exclusive} {
1.83 + set nPage 2
1.84 + for {set iFail 1} {$iFail<200} {incr iFail} {
1.85 + sqlite3_soft_heap_limit 1048576
1.86 + opendatabases
1.87 + execsql { pragma locking_mode=exclusive }
1.88 + set nRow [db one {SELECT count(*) FROM a}]
1.89 +
1.90 + # Dirty (at least) one of the pages in the cache.
1.91 + do_test ioerr5-1.$locking_mode-$iFail.1 {
1.92 + execsql {
1.93 + BEGIN EXCLUSIVE;
1.94 + INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP');
1.95 + }
1.96 + } {}
1.97 +
1.98 + # Now try to commit the transaction. Cause an IO error to occur
1.99 + # within this operation, which moves the pager into the error state.
1.100 + #
1.101 + set ::sqlite_io_error_persist 1
1.102 + set ::sqlite_io_error_pending $iFail
1.103 + do_test ioerr5-1.$locking_mode-$iFail.2 {
1.104 + set rc [catchsql {COMMIT}]
1.105 + list
1.106 + } {}
1.107 + set ::sqlite_io_error_hit 0
1.108 + set ::sqlite_io_error_persist 0
1.109 + set ::sqlite_io_error_pending 0
1.110 +
1.111 + # Read the contents of the database file into a Tcl variable.
1.112 + #
1.113 + set fd [open test.db]
1.114 + fconfigure $fd -translation binary -encoding binary
1.115 + set zDatabase [read $fd]
1.116 + close $fd
1.117 +
1.118 + # Set a very low soft-limit and then try to compile an SQL statement
1.119 + # from UTF-16 text. To do this, SQLite will need to reclaim memory
1.120 + # from the pager that is in error state. Including that associated
1.121 + # with the dirty page.
1.122 + #
1.123 + do_test ioerr5-1.$locking_mode-$iFail.3 {
1.124 + set bt [btree_from_db db]
1.125 + sqlite3_soft_heap_limit 1024
1.126 + compilesql16 "SELECT 10"
1.127 + array set stats [btree_pager_stats $bt]
1.128 +
1.129 + # If the pager made it all the way to PAGER_SYNCED state, then
1.130 + # both in-memory pages are clean. Following the calls to
1.131 + # release_memory() that were made as part of the [compilesql16]
1.132 + # above, there will be zero pages left in the cache.
1.133 + #
1.134 + # If the pager did not make it as far as PAGER_SYNCED, the two
1.135 + # in memory pages are still dirty. So there will be 2 pages left
1.136 + # in the cache following the release_memory() calls.
1.137 + #
1.138 + if {$stats(state)==5} {
1.139 + set nPage 0
1.140 + }
1.141 + expr {$stats(page)==$nPage}
1.142 + } {1}
1.143 +
1.144 + # Ensure that nothing was written to the database while reclaiming
1.145 + # memory from the pager in error state.
1.146 + #
1.147 + do_test ioerr5-1.$locking_mode-$iFail.4 {
1.148 + set fd [open test.db]
1.149 + fconfigure $fd -translation binary -encoding binary
1.150 + set zDatabase2 [read $fd]
1.151 + close $fd
1.152 + expr {$zDatabase eq $zDatabase2}
1.153 + } {1}
1.154 +
1.155 + if {$rc eq [list 0 {}]} {
1.156 + do_test ioerr5.1-$locking_mode-$iFail.3 {
1.157 + execsql { SELECT count(*) FROM a }
1.158 + } [expr $nRow+1]
1.159 + break
1.160 + }
1.161 + }
1.162 +}
1.163 +
1.164 +# Make sure this test script doesn't leave any files open.
1.165 +#
1.166 +do_test ioerr5-1.X {
1.167 + catch { db close }
1.168 + catch { db2 close }
1.169 + set sqlite_open_file_count
1.170 +} 0
1.171 +
1.172 +do_test ioerr5-2.0 {
1.173 + sqlite3 db test.db
1.174 + execsql { CREATE INDEX i1 ON a(id, name); }
1.175 +} {}
1.176 +
1.177 +foreach locking_mode {exclusive normal} {
1.178 + for {set iFail 1} {$iFail<200} {incr iFail} {
1.179 + sqlite3_soft_heap_limit 1048576
1.180 + opendatabases
1.181 + execsql { pragma locking_mode=exclusive }
1.182 + set nRow [db one {SELECT count(*) FROM a}]
1.183 +
1.184 + do_test ioerr5-2.$locking_mode-$iFail.1 {
1.185 + execsql {
1.186 + BEGIN EXCLUSIVE;
1.187 + INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP');
1.188 + }
1.189 + } {}
1.190 +
1.191 + set ::sqlite_io_error_persist 1
1.192 + set ::sqlite_io_error_pending $iFail
1.193 +
1.194 + sqlite3_release_memory 10000
1.195 +
1.196 + set error_hit $::sqlite_io_error_hit
1.197 + set ::sqlite_io_error_hit 0
1.198 + set ::sqlite_io_error_persist 0
1.199 + set ::sqlite_io_error_pending 0
1.200 + if {$error_hit} {
1.201 + do_test ioerr5-2.$locking_mode-$iFail.3a {
1.202 + catchsql COMMIT
1.203 + } {1 {disk I/O error}}
1.204 + } else {
1.205 + do_test ioerr5-2.$locking_mode-$iFail.3b {
1.206 + execsql COMMIT
1.207 + } {}
1.208 + break
1.209 + }
1.210 + }
1.211 +}
1.212 +
1.213 +# Make sure this test script doesn't leave any files open.
1.214 +#
1.215 +do_test ioerr5-2.X {
1.216 + catch { db close }
1.217 + catch { db2 close }
1.218 + set sqlite_open_file_count
1.219 +} 0
1.220 +
1.221 +sqlite3_enable_shared_cache $::enable_shared_cache
1.222 +sqlite3_soft_heap_limit $::soft_limit
1.223 +
1.224 +finish_test