os/persistentdata/persistentstorage/sqlite3api/TEST/TclScript/ioerr5.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/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