Index: third_party/sqlite/sqlite-src-3070603/test/ioerr5.test |
diff --git a/third_party/sqlite/sqlite-src-3070603/test/ioerr5.test b/third_party/sqlite/sqlite-src-3070603/test/ioerr5.test |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a430f534074207047eaf7c8ad1af10adf1850173 |
--- /dev/null |
+++ b/third_party/sqlite/sqlite-src-3070603/test/ioerr5.test |
@@ -0,0 +1,216 @@ |
+# 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'); |
+ } |
+ } {} |
+ |
+ # Open a read-only cursor on table "a". If the COMMIT below is |
+ # interrupted by a persistent IO error, the pager will transition to |
+ # PAGER_ERROR state. If there are no other read-only cursors open, |
+ # from there the pager immediately discards all cached data and |
+ # switches to PAGER_OPEN state. This read-only cursor stops that |
+ # from happening, leaving the pager stuck in PAGER_ERROR state. |
+ # |
+ set channel [db incrblob -readonly a Name [db last_insert_rowid]] |
+ |
+ # 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 { |
+ sqlite3_soft_heap_limit 1024 |
+ compilesql16 "SELECT 10" |
+ } {SQLITE_OK} |
+ |
+ close $channel |
+ |
+ # 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 |