diff -r 000000000000 -r bde4ae8d615e os/persistentdata/persistentstorage/sqlite3api/TEST/TclScript/ioerr5.test --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TclScript/ioerr5.test Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,221 @@ +# 2008 May 12 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file tests that if sqlite3_release_memory() is called to reclaim +# memory from a pager that is in the error-state, SQLite does not +# incorrectly write dirty pages out to the database (not safe to do +# once the pager is in error state). +# +# $Id: ioerr5.test,v 1.5 2008/08/28 18:35:34 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !memorymanage||!shared_cache { + finish_test + return +} + +db close + +set ::enable_shared_cache [sqlite3_enable_shared_cache 1] +set ::soft_limit [sqlite3_soft_heap_limit 1048576] + +# This procedure prepares, steps and finalizes an SQL statement via the +# UTF-16 APIs. The text representation of an SQLite error code is returned +# ("SQLITE_OK", "SQLITE_IOERR" etc.). The actual results returned by the +# SQL statement, if it is a SELECT, are not available. +# +# This can be useful for testing because it forces SQLite to make an extra +# call to sqlite3_malloc() when translating from the supplied UTF-16 to +# the UTF-8 encoding used internally. +# +proc dosql16 {zSql {db db}} { + set sql [encoding convertto unicode $zSql] + append sql "\00\00" + set stmt [sqlite3_prepare16 $db $sql -1 {}] + sqlite3_step $stmt + set rc [sqlite3_finalize $stmt] +} + +proc compilesql16 {zSql {db db}} { + set sql [encoding convertto unicode $zSql] + append sql "\00\00" + set stmt [sqlite3_prepare16 $db $sql -1 {}] + set rc [sqlite3_finalize $stmt] +} + +# Open two database connections (handle db and db2) to database "test.db". +# +proc opendatabases {} { + catch {db close} + catch {db2 close} + sqlite3 db test.db + sqlite3 db2 test.db + db2 cache size 0 + db cache size 0 + execsql { + pragma page_size=512; + pragma auto_vacuum=2; + pragma cache_size=16; + } +} + +# Open two database connections and create a single table in the db. +# +do_test ioerr5-1.0 { + opendatabases + execsql { CREATE TABLE A(Id INTEGER, Name TEXT) } +} {} + +foreach locking_mode {normal exclusive} { + set nPage 2 + for {set iFail 1} {$iFail<200} {incr iFail} { + sqlite3_soft_heap_limit 1048576 + opendatabases + execsql { pragma locking_mode=exclusive } + set nRow [db one {SELECT count(*) FROM a}] + + # Dirty (at least) one of the pages in the cache. + do_test ioerr5-1.$locking_mode-$iFail.1 { + execsql { + BEGIN EXCLUSIVE; + INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP'); + } + } {} + + # Now try to commit the transaction. Cause an IO error to occur + # within this operation, which moves the pager into the error state. + # + set ::sqlite_io_error_persist 1 + set ::sqlite_io_error_pending $iFail + do_test ioerr5-1.$locking_mode-$iFail.2 { + set rc [catchsql {COMMIT}] + list + } {} + set ::sqlite_io_error_hit 0 + set ::sqlite_io_error_persist 0 + set ::sqlite_io_error_pending 0 + + # Read the contents of the database file into a Tcl variable. + # + set fd [open test.db] + fconfigure $fd -translation binary -encoding binary + set zDatabase [read $fd] + close $fd + + # Set a very low soft-limit and then try to compile an SQL statement + # from UTF-16 text. To do this, SQLite will need to reclaim memory + # from the pager that is in error state. Including that associated + # with the dirty page. + # + do_test ioerr5-1.$locking_mode-$iFail.3 { + set bt [btree_from_db db] + sqlite3_soft_heap_limit 1024 + compilesql16 "SELECT 10" + array set stats [btree_pager_stats $bt] + + # If the pager made it all the way to PAGER_SYNCED state, then + # both in-memory pages are clean. Following the calls to + # release_memory() that were made as part of the [compilesql16] + # above, there will be zero pages left in the cache. + # + # If the pager did not make it as far as PAGER_SYNCED, the two + # in memory pages are still dirty. So there will be 2 pages left + # in the cache following the release_memory() calls. + # + if {$stats(state)==5} { + set nPage 0 + } + expr {$stats(page)==$nPage} + } {1} + + # Ensure that nothing was written to the database while reclaiming + # memory from the pager in error state. + # + do_test ioerr5-1.$locking_mode-$iFail.4 { + set fd [open test.db] + fconfigure $fd -translation binary -encoding binary + set zDatabase2 [read $fd] + close $fd + expr {$zDatabase eq $zDatabase2} + } {1} + + if {$rc eq [list 0 {}]} { + do_test ioerr5.1-$locking_mode-$iFail.3 { + execsql { SELECT count(*) FROM a } + } [expr $nRow+1] + break + } + } +} + +# Make sure this test script doesn't leave any files open. +# +do_test ioerr5-1.X { + catch { db close } + catch { db2 close } + set sqlite_open_file_count +} 0 + +do_test ioerr5-2.0 { + sqlite3 db test.db + execsql { CREATE INDEX i1 ON a(id, name); } +} {} + +foreach locking_mode {exclusive normal} { + for {set iFail 1} {$iFail<200} {incr iFail} { + sqlite3_soft_heap_limit 1048576 + opendatabases + execsql { pragma locking_mode=exclusive } + set nRow [db one {SELECT count(*) FROM a}] + + do_test ioerr5-2.$locking_mode-$iFail.1 { + execsql { + BEGIN EXCLUSIVE; + INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP'); + } + } {} + + set ::sqlite_io_error_persist 1 + set ::sqlite_io_error_pending $iFail + + sqlite3_release_memory 10000 + + set error_hit $::sqlite_io_error_hit + set ::sqlite_io_error_hit 0 + set ::sqlite_io_error_persist 0 + set ::sqlite_io_error_pending 0 + if {$error_hit} { + do_test ioerr5-2.$locking_mode-$iFail.3a { + catchsql COMMIT + } {1 {disk I/O error}} + } else { + do_test ioerr5-2.$locking_mode-$iFail.3b { + execsql COMMIT + } {} + break + } + } +} + +# Make sure this test script doesn't leave any files open. +# +do_test ioerr5-2.X { + catch { db close } + catch { db2 close } + set sqlite_open_file_count +} 0 + +sqlite3_enable_shared_cache $::enable_shared_cache +sqlite3_soft_heap_limit $::soft_limit + +finish_test