blob: 104dac37c8aeec9d7c0649ee0901372022decd6d [file] [log] [blame]
drh81a20f22001-10-12 17:30:04 +00001# 2001 October 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# This file implements regression tests for SQLite library. The
12# focus of this file is testing for correct handling of I/O errors
13# such as writes failing because the disk is full.
14#
15# The tests in this file use special facilities that are only
16# available in the SQLite test fixture.
17#
danielk1977ca670712005-01-19 03:47:15 +000018# $Id: ioerr.test,v 1.16 2005/01/19 03:47:16 danielk1977 Exp $
drh81a20f22001-10-12 17:30:04 +000019
20set testdir [file dirname $argv0]
21source $testdir/tester.tcl
22
danielk19778b60e0f2005-01-12 09:10:39 +000023# Usage: do_ioerr_test <test number> <options...>
24#
25# The first argument, <test number>, is an integer used to name the
26# tests executed by this proc. Options are as follows:
27#
28# -tclprep TCL script to run to prepare test.
29# -sqlprep SQL script to run to prepare test.
30# -tclbody TCL script to run with IO error simulation.
31# -sqlbody TCL script to run with IO error simulation.
32# -exclude List of 'N' values not to test.
danielk1977aca790a2005-01-13 11:07:52 +000033# -start Value of 'N' to begin with (default 1)
danielk19778b60e0f2005-01-12 09:10:39 +000034#
35proc do_ioerr_test {tn args} {
36 array set ::ioerropts $args
danielk197759adfaa2005-01-08 02:35:44 +000037
danielk19778b60e0f2005-01-12 09:10:39 +000038 set ::go 1
danielk1977aca790a2005-01-13 11:07:52 +000039 if {![info exists ::ioerropts(-start)]} {
40 set ::ioerropts(-start) 1
41 }
42 for {set n $::ioerropts(-start)} {$::go} {incr n} {
danielk19778b60e0f2005-01-12 09:10:39 +000043
44 if {[info exists ::ioerropts(-exclude)]} {
45 if {[lsearch $::ioerropts(-exclude) $n]!=-1} continue
46 }
danielk197759adfaa2005-01-08 02:35:44 +000047
danielk19778b60e0f2005-01-12 09:10:39 +000048 do_test ioerr-$tn.$n.1 {
49 set ::sqlite_io_error_pending 0
danielk1977aca790a2005-01-13 11:07:52 +000050 catch {db close}
danielk19778b60e0f2005-01-12 09:10:39 +000051 catch {file delete -force test.db}
52 catch {file delete -force test.db-journal}
53 catch {file delete -force test2.db}
54 catch {file delete -force test2.db-journal}
55
56 set ::DB [sqlite3 db test.db]
57
58 if {[info exists ::ioerropts(-tclprep)]} {
59 eval $::ioerropts(-tclprep)
60 }
61 if {[info exists ::ioerropts(-sqlprep)]} {
62 execsql $::ioerropts(-sqlprep)
63 }
64 expr 0
65 } {0}
66
67 do_test ioerr-$tn.$n.2 [subst {
68 set ::sqlite_io_error_pending $n
69 }] $n
70
71 set ::ioerrorbody {}
72 if {[info exists ::ioerropts(-tclbody)]} {
73 append ::ioerrorbody "$::ioerropts(-tclbody)\n"
74 }
75 if {[info exists ::ioerropts(-sqlbody)]} {
76 append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}"
77 }
78 do_test ioerr-$tn.$n.3 {
79 set r [catch $::ioerrorbody msg]
80 set ::go [expr {$::sqlite_io_error_pending<=0}]
81 expr {$::sqlite_io_error_pending>0 || $r!=0}
82 } {1}
83 }
84 set ::sqlite_io_error_pending 0
danielk1977aca790a2005-01-13 11:07:52 +000085 unset ::ioerropts
drh81a20f22001-10-12 17:30:04 +000086}
danielk19778b60e0f2005-01-12 09:10:39 +000087
88# If SQLITE_DEFAULT_AUTOVACUUM is set to true, then a simulated IO error
89# on the 8th IO operation in the SQL script below doesn't report an error.
90#
91# This is because the 8th IO call attempts to read page 2 of the database
92# file when the file on disk is only 1 page. The pager layer detects that
93# this has happened and suppresses the error returned by the OS layer.
94#
95do_ioerr_test 1 -sqlprep {
96 SELECT * FROM sqlite_master;
97} -sqlbody {
98 CREATE TABLE t1(a,b,c);
99 SELECT * FROM sqlite_master;
100 BEGIN TRANSACTION;
101 INSERT INTO t1 VALUES(1,2,3);
102 INSERT INTO t1 VALUES(4,5,6);
103 ROLLBACK;
104 SELECT * FROM t1;
105 BEGIN TRANSACTION;
106 INSERT INTO t1 VALUES(1,2,3);
107 INSERT INTO t1 VALUES(4,5,6);
108 COMMIT;
109 SELECT * FROM t1;
110 DELETE FROM t1 WHERE a<100;
danielk19774e17d142005-01-16 09:06:33 +0000111} -exclude [expr [string match [execsql {pragma auto_vacuum}] 1] ? 8 : 0]
danielk19778b60e0f2005-01-12 09:10:39 +0000112
drh81a20f22001-10-12 17:30:04 +0000113
drh2e6d11b2003-04-25 15:37:57 +0000114proc cksum {{db db}} {
danielk1977369f27e2004-06-15 11:40:04 +0000115 set txt [$db eval {
116 SELECT name, type, sql FROM sqlite_master order by name
117 }]\n
118 foreach tbl [$db eval {
119 SELECT name FROM sqlite_master WHERE type='table' order by name
120 }] {
drh2e6d11b2003-04-25 15:37:57 +0000121 append txt [$db eval "SELECT * FROM $tbl"]\n
122 }
123 foreach prag {default_synchronous default_cache_size} {
124 append txt $prag-[$db eval "PRAGMA $prag"]\n
125 }
126 set cksum [string length $txt]-[md5 $txt]
127 # puts $cksum-[file size test.db]
128 return $cksum
129}
130
131set ::go 1
132for {set n 1} {$go} {incr n} {
danielk19777701e812005-01-10 12:59:51 +0000133 if {$n==24} breakpoint
drh2e6d11b2003-04-25 15:37:57 +0000134 do_test ioerr-2.$n.1 {
135 set ::sqlite_io_error_pending 0
136 db close
137 catch {file delete -force test.db}
138 catch {file delete -force test.db-journal}
danielk19778b60e0f2005-01-12 09:10:39 +0000139 catch {file delete -force test2.db}
140 catch {file delete -force test2.db-journal}
drhef4ac8f2004-06-19 00:16:31 +0000141 sqlite3 db test.db
drh2e6d11b2003-04-25 15:37:57 +0000142 execsql {
143 BEGIN;
144 CREATE TABLE t1(a, b, c);
145 INSERT INTO t1 VALUES(1, randstr(5,50), randstr(5,50));
146 INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
147 INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
148 INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
149 INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
150 INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
151 INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
152 INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
danielk19777701e812005-01-10 12:59:51 +0000153 INSERT INTO t1 VALUES(1, randstr(600,600), randstr(600,600));
drh2e6d11b2003-04-25 15:37:57 +0000154 CREATE TABLE t2 AS SELECT * FROM t1;
155 CREATE TABLE t3 AS SELECT * FROM t1;
156 COMMIT;
157 DROP TABLE t2;
158 }
159 set ::cksum [cksum]
160 execsql {
161 SELECT name FROM sqlite_master WHERE type='table'
162 }
163 } {t1 t3}
164 do_test ioerr-2.$n.2 [subst {
165 set ::sqlite_io_error_pending $n
166 }] $n
167 do_test ioerr-2.$n.3 {
168 set r [catch {db eval {
169 VACUUM;
170 }} msg]
drh2e6d11b2003-04-25 15:37:57 +0000171 set ::go [expr {$::sqlite_io_error_pending<=0}]
172 expr {$::sqlite_io_error_pending>0 || $r!=0}
173 set ::sqlite_io_error_pending 0
174 db close
drhef4ac8f2004-06-19 00:16:31 +0000175 sqlite3 db test.db
drh2e6d11b2003-04-25 15:37:57 +0000176 cksum
177 } $cksum
178}
179set ::sqlite_io_error_pending 0
drh81a20f22001-10-12 17:30:04 +0000180
danielk197728129562005-01-11 10:25:06 +0000181
danielk19778b60e0f2005-01-12 09:10:39 +0000182do_ioerr_test 3 -tclprep {
183 execsql {
184 PRAGMA cache_size = 10;
185 BEGIN;
186 CREATE TABLE abc(a);
187 INSERT INTO abc VALUES(randstr(1500,1500)); -- Page 4 is overflow
188 }
189 for {set i 0} {$i<150} {incr i} {
190 execsql {
191 INSERT INTO abc VALUES(randstr(100,100));
danielk197701427a62005-01-11 13:02:33 +0000192 }
danielk19778b60e0f2005-01-12 09:10:39 +0000193 }
194 execsql COMMIT
195} -sqlbody {
196 CREATE TABLE abc2(a);
197 BEGIN;
198 DELETE FROM abc WHERE length(a)>100;
199 UPDATE abc SET a = randstr(90,90);
200 COMMIT;
201 CREATE TABLE abc3(a);
danielk1977aac0a382005-01-16 11:07:06 +0000202}
danielk19778b60e0f2005-01-12 09:10:39 +0000203
204# Test IO errors that can occur retrieving a record header that flows over
205# onto an overflow page.
206do_ioerr_test 4 -tclprep {
207 set sql "CREATE TABLE abc(a1"
208 for {set i 2} {$i<1300} {incr i} {
209 append sql ", a$i"
210 }
211 append sql ");"
212 execsql $sql
213 execsql {INSERT INTO abc (a1) VALUES(NULL)}
214} -sqlbody {
215 SELECT * FROM abc;
216}
217
218# Test IO errors that may occur during a multi-file commit.
danielk1977aca790a2005-01-13 11:07:52 +0000219#
danielk1977aac0a382005-01-16 11:07:06 +0000220# Tests 8 and 17 are excluded when auto-vacuum is enabled for the same
221# reason as in test cases ioerr-1.XXX
222set ex ""
223if {[string match [execsql {pragma auto_vacuum}] 1]} {
224 set ex [list 8 17]
225}
danielk19778b60e0f2005-01-12 09:10:39 +0000226do_ioerr_test 5 -sqlprep {
227 ATTACH 'test2.db' AS test2;
228} -sqlbody {
229 BEGIN;
230 CREATE TABLE t1(a,b,c);
231 CREATE TABLE test2.t2(a,b,c);
232 COMMIT;
danielk1977aac0a382005-01-16 11:07:06 +0000233} -exclude $ex
danielk1977aca790a2005-01-13 11:07:52 +0000234
235# Test IO errors when replaying two hot journals from a 2-file
236# transaction. This test only runs on UNIX.
danielk1977ac245ec2005-01-14 13:50:11 +0000237if {$tcl_platform(platform)=="unix" && [file exists ./crashtest]} {
danielk1977aca790a2005-01-13 11:07:52 +0000238 do_ioerr_test 6 -tclprep {
239 set rc [crashsql 2 test2.db-journal {
240 ATTACH 'test2.db' as aux;
241 PRAGMA cache_size = 10;
242 BEGIN;
243 CREATE TABLE aux.t2(a, b, c);
244 CREATE TABLE t1(a, b, c);
245 COMMIT;
246 }]
247 if {$rc!="1 {child process exited abnormally}"} {
248 error "Wrong error message: $rc"
249 }
250 } -sqlbody {
251 ATTACH 'test2.db' as aux;
252 SELECT * FROM t1;
253 SELECT * FROM t2;
254 }
danielk19778b60e0f2005-01-12 09:10:39 +0000255}
danielk197701427a62005-01-11 13:02:33 +0000256
danielk1977aca790a2005-01-13 11:07:52 +0000257# Test handling of IO errors that occur while rolling back hot journal
258# files.
danielk1977ca670712005-01-19 03:47:15 +0000259#
260# These tests can't be run on windows because the windows version of
261# SQLite holds a mandatory exclusive lock on journal files it has open.
262#
263if {$tcl_platform(platform)!="windows"} {
264 do_ioerr_test 7 -tclprep {
265 db close
266 sqlite3 db2 test2.db
267 db2 eval {
268 PRAGMA synchronous = 0;
269 CREATE TABLE t1(a, b);
270 INSERT INTO t1 VALUES(1, 2);
271 BEGIN;
272 INSERT INTO t1 VALUES(3, 4);
273 }
274 file copy -force test2.db test.db
275 file copy -force test2.db-journal test.db-journal
276 db2 close
277 } -tclbody {
278 sqlite3 db test.db
279 db eval {
280 SELECT * FROM t1;
281 }
282 } -exclude 1
283}
danielk1977aca790a2005-01-13 11:07:52 +0000284
285# do_ioerr_test 15 -sqlprep {
286# CREATE TABLE abc(a UNIQUE, b, c);
287# INSERT INTO abc VALUES(1, 2, 3);
288# } -sqlbody {
289# BEGIN;
290# INSERT INTO abc VALUES(1, 2, 3);
291# COMMIT;
292# }
293
drh81a20f22001-10-12 17:30:04 +0000294finish_test
danielk1977aca790a2005-01-13 11:07:52 +0000295
296