Annotation of embedaddon/sqlite3/test/ioerr5.test, revision 1.1.1.1

1.1       misho       1: # 2008 May 12
                      2: #
                      3: # The author disclaims copyright to this source code.  In place of
                      4: # a legal notice, here is a blessing:
                      5: #
                      6: #    May you do good and not evil.
                      7: #    May you find forgiveness for yourself and forgive others.
                      8: #    May you share freely, never taking more than you give.
                      9: #
                     10: #***********************************************************************
                     11: #
                     12: # This file tests that if sqlite3_release_memory() is called to reclaim
                     13: # memory from a pager that is in the error-state, SQLite does not 
                     14: # incorrectly write dirty pages out to the database (not safe to do
                     15: # once the pager is in error state).
                     16: #
                     17: # $Id: ioerr5.test,v 1.5 2008/08/28 18:35:34 danielk1977 Exp $
                     18: 
                     19: set testdir [file dirname $argv0]
                     20: source $testdir/tester.tcl
                     21: 
                     22: ifcapable !memorymanage||!shared_cache {
                     23:   finish_test
                     24:   return
                     25: }
                     26: 
                     27: db close
                     28: 
                     29: set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
                     30: set ::soft_limit [sqlite3_soft_heap_limit 1048576]
                     31: 
                     32: # This procedure prepares, steps and finalizes an SQL statement via the
                     33: # UTF-16 APIs. The text representation of an SQLite error code is returned
                     34: # ("SQLITE_OK", "SQLITE_IOERR" etc.). The actual results returned by the
                     35: # SQL statement, if it is a SELECT, are not available.
                     36: #
                     37: # This can be useful for testing because it forces SQLite to make an extra 
                     38: # call to sqlite3_malloc() when translating from the supplied UTF-16 to
                     39: # the UTF-8 encoding used internally.
                     40: #
                     41: proc dosql16 {zSql {db db}} {
                     42:   set sql [encoding convertto unicode $zSql]
                     43:   append sql "\00\00"
                     44:   set stmt [sqlite3_prepare16 $db $sql -1 {}]
                     45:   sqlite3_step $stmt
                     46:   set rc [sqlite3_finalize $stmt]
                     47: }
                     48: 
                     49: proc compilesql16 {zSql {db db}} {
                     50:   set sql [encoding convertto unicode $zSql]
                     51:   append sql "\00\00"
                     52:   set stmt [sqlite3_prepare16 $db $sql -1 {}]
                     53:   set rc [sqlite3_finalize $stmt]
                     54: }
                     55: 
                     56: # Open two database connections (handle db and db2) to database "test.db".
                     57: #
                     58: proc opendatabases {} {
                     59:   catch {db close}
                     60:   catch {db2 close}
                     61:   sqlite3 db test.db
                     62:   sqlite3 db2 test.db
                     63:   db2 cache size 0
                     64:   db cache size 0
                     65:   execsql {
                     66:     pragma page_size=512;
                     67:     pragma auto_vacuum=2;
                     68:     pragma cache_size=16;
                     69:   }
                     70: }
                     71: 
                     72: # Open two database connections and create a single table in the db.
                     73: #
                     74: do_test ioerr5-1.0 {
                     75:   opendatabases
                     76:   execsql { CREATE TABLE A(Id INTEGER, Name TEXT) }
                     77: } {}
                     78: 
                     79: foreach locking_mode {normal exclusive} {
                     80:   set nPage 2
                     81:   for {set iFail 1} {$iFail<200} {incr iFail} {
                     82:     sqlite3_soft_heap_limit 1048576
                     83:     opendatabases
                     84:     execsql { pragma locking_mode=exclusive }
                     85:     set nRow [db one {SELECT count(*) FROM a}]
                     86:   
                     87:     # Dirty (at least) one of the pages in the cache.
                     88:     do_test ioerr5-1.$locking_mode-$iFail.1 {
                     89:       execsql {
                     90:         BEGIN EXCLUSIVE;
                     91:         INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP');
                     92:       }
                     93:     } {}
                     94: 
                     95:     # Open a read-only cursor on table "a". If the COMMIT below is
                     96:     # interrupted by a persistent IO error, the pager will transition to 
                     97:     # PAGER_ERROR state. If there are no other read-only cursors open,
                     98:     # from there the pager immediately discards all cached data and 
                     99:     # switches to PAGER_OPEN state. This read-only cursor stops that
                    100:     # from happening, leaving the pager stuck in PAGER_ERROR state.
                    101:     #
                    102:     set channel [db incrblob -readonly a Name [db last_insert_rowid]]
                    103:   
                    104:     # Now try to commit the transaction. Cause an IO error to occur
                    105:     # within this operation, which moves the pager into the error state.
                    106:     #
                    107:     set ::sqlite_io_error_persist 1
                    108:     set ::sqlite_io_error_pending $iFail
                    109:     do_test ioerr5-1.$locking_mode-$iFail.2 {
                    110:       set rc [catchsql {COMMIT}]
                    111:       list
                    112:     } {}
                    113:     set ::sqlite_io_error_hit 0
                    114:     set ::sqlite_io_error_persist 0
                    115:     set ::sqlite_io_error_pending 0
                    116:   
                    117:     # Read the contents of the database file into a Tcl variable.
                    118:     #
                    119:     set fd [open test.db]
                    120:     fconfigure $fd -translation binary -encoding binary
                    121:     set zDatabase [read $fd]
                    122:     close $fd
                    123: 
                    124:     # Set a very low soft-limit and then try to compile an SQL statement 
                    125:     # from UTF-16 text. To do this, SQLite will need to reclaim memory
                    126:     # from the pager that is in error state. Including that associated
                    127:     # with the dirty page.
                    128:     #
                    129:     do_test ioerr5-1.$locking_mode-$iFail.3 {
                    130:       sqlite3_soft_heap_limit 1024
                    131:       compilesql16 "SELECT 10"
                    132:     } {SQLITE_OK}
                    133: 
                    134:     close $channel
                    135: 
                    136:     # Ensure that nothing was written to the database while reclaiming
                    137:     # memory from the pager in error state.
                    138:     #
                    139:     do_test ioerr5-1.$locking_mode-$iFail.4 {
                    140:       set fd [open test.db]
                    141:       fconfigure $fd -translation binary -encoding binary
                    142:       set zDatabase2 [read $fd]
                    143:       close $fd
                    144:       expr {$zDatabase eq $zDatabase2}
                    145:     } {1}
                    146: 
                    147:     if {$rc eq [list 0 {}]} {
                    148:       do_test ioerr5.1-$locking_mode-$iFail.3 {
                    149:         execsql { SELECT count(*) FROM a }
                    150:       } [expr $nRow+1]
                    151:       break
                    152:     }
                    153:   }
                    154: }
                    155: 
                    156: # Make sure this test script doesn't leave any files open.
                    157: #
                    158: do_test ioerr5-1.X {
                    159:   catch { db close }
                    160:   catch { db2 close }
                    161:   set sqlite_open_file_count
                    162: } 0
                    163: 
                    164: do_test ioerr5-2.0 {
                    165:   sqlite3 db test.db
                    166:   execsql { CREATE INDEX i1 ON a(id, name); }
                    167: } {}
                    168: 
                    169: foreach locking_mode {exclusive normal} {
                    170:   for {set iFail 1} {$iFail<200} {incr iFail} {
                    171:     sqlite3_soft_heap_limit 1048576
                    172:     opendatabases
                    173:     execsql { pragma locking_mode=exclusive }
                    174:     set nRow [db one {SELECT count(*) FROM a}]
                    175:   
                    176:     do_test ioerr5-2.$locking_mode-$iFail.1 {
                    177:       execsql {
                    178:         BEGIN EXCLUSIVE;
                    179:         INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP');
                    180:       }
                    181:     } {}
                    182: 
                    183:     set ::sqlite_io_error_persist 1
                    184:     set ::sqlite_io_error_pending $iFail
                    185: 
                    186:     sqlite3_release_memory 10000
                    187: 
                    188:     set error_hit $::sqlite_io_error_hit
                    189:     set ::sqlite_io_error_hit 0
                    190:     set ::sqlite_io_error_persist 0
                    191:     set ::sqlite_io_error_pending 0
                    192:     if {$error_hit} {
                    193:       do_test ioerr5-2.$locking_mode-$iFail.3a {
                    194:         catchsql COMMIT
                    195:       } {1 {disk I/O error}}
                    196:     } else {
                    197:       do_test ioerr5-2.$locking_mode-$iFail.3b {
                    198:         execsql COMMIT
                    199:       } {}
                    200:       break
                    201:     }
                    202:   }
                    203: }
                    204: 
                    205: # Make sure this test script doesn't leave any files open.
                    206: #
                    207: do_test ioerr5-2.X {
                    208:   catch { db close }
                    209:   catch { db2 close }
                    210:   set sqlite_open_file_count
                    211: } 0
                    212: 
                    213: sqlite3_enable_shared_cache $::enable_shared_cache
                    214: sqlite3_soft_heap_limit $::soft_limit
                    215: 
                    216: finish_test

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>