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