1: # 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: #
18: # $Id: ioerr.test,v 1.1.1.1 2012/02/21 17:04:16 misho Exp $
19:
20: set testdir [file dirname $argv0]
21: source $testdir/tester.tcl
22:
23: # If SQLITE_DEFAULT_AUTOVACUUM is set to true, then a simulated IO error
24: # on the 8th IO operation in the SQL script below doesn't report an error.
25: #
26: # This is because the 8th IO call attempts to read page 2 of the database
27: # file when the file on disk is only 1 page. The pager layer detects that
28: # this has happened and suppresses the error returned by the OS layer.
29: #
30: do_ioerr_test ioerr-1 -erc 1 -ckrefcount 1 -sqlprep {
31: SELECT * FROM sqlite_master;
32: } -sqlbody {
33: CREATE TABLE t1(a,b,c);
34: SELECT * FROM sqlite_master;
35: BEGIN TRANSACTION;
36: INSERT INTO t1 VALUES(1,2,3);
37: INSERT INTO t1 VALUES(4,5,6);
38: ROLLBACK;
39: SELECT * FROM t1;
40: BEGIN TRANSACTION;
41: INSERT INTO t1 VALUES(1,2,3);
42: INSERT INTO t1 VALUES(4,5,6);
43: COMMIT;
44: SELECT * FROM t1;
45: DELETE FROM t1 WHERE a<100;
46: } -exclude [expr [string match [execsql {pragma auto_vacuum}] 1] ? 4 : 0]
47:
48: # Test for IO errors during a VACUUM.
49: #
50: # The first IO call is excluded from the test. This call attempts to read
51: # the file-header of the temporary database used by VACUUM. Since the
52: # database doesn't exist at that point, the IO error is not detected.
53: #
54: # Additionally, if auto-vacuum is enabled, the 12th IO error is not
55: # detected. Same reason as the 8th in the test case above.
56: #
57: ifcapable vacuum {
58: do_ioerr_test ioerr-2 -cksum true -ckrefcount true -sqlprep {
59: BEGIN;
60: CREATE TABLE t1(a, b, c);
61: INSERT INTO t1 VALUES(1, randstr(50,50), randstr(50,50));
62: INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
63: INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
64: INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
65: INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
66: INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
67: INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
68: INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
69: INSERT INTO t1 VALUES(1, randstr(600,600), randstr(600,600));
70: CREATE TABLE t2 AS SELECT * FROM t1;
71: CREATE TABLE t3 AS SELECT * FROM t1;
72: COMMIT;
73: DROP TABLE t2;
74: } -sqlbody {
75: VACUUM;
76: } -exclude [list \
77: 1 [expr [string match [execsql {pragma auto_vacuum}] 1]?9:-1]]
78: }
79:
80: do_ioerr_test ioerr-3 -ckrefcount true -tclprep {
81: execsql {
82: PRAGMA cache_size = 10;
83: BEGIN;
84: CREATE TABLE abc(a);
85: INSERT INTO abc VALUES(randstr(1500,1500)); -- Page 4 is overflow
86: }
87: for {set i 0} {$i<150} {incr i} {
88: execsql {
89: INSERT INTO abc VALUES(randstr(100,100));
90: }
91: }
92: execsql COMMIT
93: } -sqlbody {
94: CREATE TABLE abc2(a);
95: BEGIN;
96: DELETE FROM abc WHERE length(a)>100;
97: UPDATE abc SET a = randstr(90,90);
98: COMMIT;
99: CREATE TABLE abc3(a);
100: }
101:
102: # Test IO errors that can occur retrieving a record header that flows over
103: # onto an overflow page.
104: do_ioerr_test ioerr-4 -ckrefcount true -tclprep {
105: set sql "CREATE TABLE abc(a1"
106: for {set i 2} {$i<1300} {incr i} {
107: append sql ", a$i"
108: }
109: append sql ");"
110: execsql $sql
111: execsql {INSERT INTO abc (a1) VALUES(NULL)}
112: } -sqlbody {
113: SELECT * FROM abc;
114: }
115:
116:
117: # Test IO errors that may occur during a multi-file commit.
118: #
119: # Tests 8 and 17 are excluded when auto-vacuum is enabled for the same
120: # reason as in test cases ioerr-1.XXX
121: ifcapable attach {
122: set ex ""
123: if {[string match [execsql {pragma auto_vacuum}] 1]} {
124: set ex [list 4 17]
125: }
126: do_ioerr_test ioerr-5 -restoreprng 0 -ckrefcount true -sqlprep {
127: ATTACH 'test2.db' AS test2;
128: } -sqlbody {
129: BEGIN;
130: CREATE TABLE t1(a,b,c);
131: CREATE TABLE test2.t2(a,b,c);
132: COMMIT;
133: } -exclude $ex
134: }
135:
136: # Test IO errors when replaying two hot journals from a 2-file
137: # transaction. This test only runs on UNIX.
138: #
139: # It cannot be run under the "exclusive" permutation. In that case, the
140: # locks held by the connection in the local (this) process prevent a
141: # second connection from attempting the multi-file transaction.
142: #
143: ifcapable crashtest&&attach {
144: if {![catch {sqlite3 -has-codec} r] && !$r && [permutation]!="exclusive"} {
145: do_ioerr_test ioerr-6 -ckrefcount true -tclprep {
146: execsql {
147: ATTACH 'test2.db' as aux;
148: CREATE TABLE tx(a, b);
149: CREATE TABLE aux.ty(a, b);
150: }
151: set rc [crashsql -delay 2 -file test2.db-journal {
152: ATTACH 'test2.db' as aux;
153: PRAGMA cache_size = 10;
154: BEGIN;
155: CREATE TABLE aux.t2(a, b, c);
156: CREATE TABLE t1(a, b, c);
157: COMMIT;
158: }]
159: if {$rc!="1 {child process exited abnormally}"} {
160: error "Wrong error message: $rc"
161: }
162: } -sqlbody {
163: SELECT * FROM sqlite_master;
164: SELECT * FROM aux.sqlite_master;
165: }
166: }
167: }
168:
169: # Test handling of IO errors that occur while rolling back hot journal
170: # files.
171: #
172: # These tests can't be run on windows because the windows version of
173: # SQLite holds a mandatory exclusive lock on journal files it has open.
174: #
175: if {$tcl_platform(platform)!="windows"} {
176: do_ioerr_test ioerr-7 -tclprep {
177: db close
178: sqlite3 db2 test2.db
179: db2 eval {
180: PRAGMA synchronous = 0;
181: CREATE TABLE t1(a, b);
182: INSERT INTO t1 VALUES(1, 2);
183: BEGIN;
184: INSERT INTO t1 VALUES(3, 4);
185: }
186: forcecopy test2.db test.db
187: forcecopy test2.db-journal test.db-journal
188: db2 close
189: } -tclbody {
190: sqlite3 db test.db
191: db eval {
192: SELECT * FROM t1;
193: }
194: } -exclude 1
195: }
196:
197: # For test coverage: Cause an I/O failure while trying to read a
198: # short field (one that fits into a Mem buffer without mallocing
199: # for space).
200: #
201: do_ioerr_test ioerr-8 -ckrefcount true -tclprep {
202: execsql {
203: CREATE TABLE t1(a,b,c);
204: INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
205: }
206: db close
207: sqlite3 db test.db
208: } -sqlbody {
209: SELECT c FROM t1;
210: }
211:
212: # For test coverage: Cause an IO error whilst reading the master-journal
213: # name from a journal file.
214: if {$tcl_platform(platform)=="unix"} {
215: do_ioerr_test ioerr-9 -ckrefcount true -tclprep {
216: execsql {
217: CREATE TABLE t1(a,b,c);
218: INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
219: BEGIN;
220: INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
221: }
222: forcecopy test.db-journal test2.db-journal
223: execsql {
224: COMMIT;
225: }
226: forcecopy test2.db-journal test.db-journal
227: set f [open test.db-journal a]
228: fconfigure $f -encoding binary
229: puts -nonewline $f "hello"
230: puts -nonewline $f "\x00\x00\x00\x05\x01\x02\x03\x04"
231: puts -nonewline $f "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
232: close $f
233: } -sqlbody {
234: SELECT a FROM t1;
235: }
236: }
237:
238: # For test coverage: Cause an IO error during statement playback (i.e.
239: # a constraint).
240: do_ioerr_test ioerr-10 -ckrefcount true -tclprep {
241: execsql {
242: BEGIN;
243: CREATE TABLE t1(a PRIMARY KEY, b);
244: }
245: for {set i 0} {$i < 500} {incr i} {
246: execsql {INSERT INTO t1 VALUES(:i, 'hello world');}
247: }
248: execsql {
249: COMMIT;
250: }
251: } -tclbody {
252:
253: catch {execsql {
254: BEGIN;
255: INSERT INTO t1 VALUES('abc', 123);
256: INSERT INTO t1 VALUES('def', 123);
257: INSERT INTO t1 VALUES('ghi', 123);
258: INSERT INTO t1 SELECT (a+500)%900, 'good string' FROM t1;
259: }} msg
260:
261: if {$msg != "column a is not unique"} {
262: error $msg
263: }
264: }
265:
266: # Assertion fault bug reported by alex dimitrov.
267: #
268: do_ioerr_test ioerr-11 -ckrefcount true -erc 1 -sqlprep {
269: CREATE TABLE A(Id INTEGER, Name TEXT);
270: INSERT INTO A(Id, Name) VALUES(1, 'Name');
271: } -sqlbody {
272: UPDATE A SET Id = 2, Name = 'Name2' WHERE Id = 1;
273: }
274:
275: # Test that an io error encountered in a sync() caused by a call to
276: # sqlite3_release_memory() is handled Ok. Only try this if
277: # memory-management is enabled.
278: #
279: ifcapable memorymanage {
280: do_ioerr_test memmanage-ioerr1 -ckrefcount true -sqlprep {
281: BEGIN;
282: CREATE TABLE t1(a, b, c);
283: INSERT INTO t1 VALUES(randstr(50,50), randstr(100,100), randstr(10,10));
284: INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
285: INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
286: INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
287: INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
288: INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
289: } -tclbody {
290: sqlite3_release_memory
291: } -sqlbody {
292: COMMIT;
293: }
294: }
295:
296: ifcapable pager_pragmas&&autovacuum {
297: do_ioerr_test ioerr-12 -ckrefcount true -erc 1 -sqlprep {
298: PRAGMA page_size = 512;
299: PRAGMA auto_vacuum = incremental;
300: CREATE TABLE t1(x);
301: INSERT INTO t1 VALUES( randomblob(1 * (512-4)) );
302: INSERT INTO t1 VALUES( randomblob(110 * (512-4)) );
303: INSERT INTO t1 VALUES( randomblob(2 * (512-4)) );
304: INSERT INTO t1 VALUES( randomblob(110 * (512-4)) );
305: INSERT INTO t1 VALUES( randomblob(3 * (512-4)) );
306: DELETE FROM t1 WHERE rowid = 3;
307: PRAGMA incremental_vacuum = 2;
308: DELETE FROM t1 WHERE rowid = 1;
309: } -sqlbody {
310: PRAGMA incremental_vacuum = 1;
311: }
312: }
313:
314: # Usually, after a new page is allocated from the end of the file, it does
315: # not need to be written to the journal. The exception is when the new page
316: # shares its sector with an existing page that does need to be journalled.
317: # This test case provokes this condition to test for the sake of coverage
318: # that an IO error while journalling the coresident page is handled correctly.
319: #
320: sqlite3_simulate_device -char {} -sectorsize 2048
321: do_ioerr_test ioerr-12 -ckrefcount true -erc 1 -tclprep {
322: db close
323: sqlite3 db test.db -vfs devsym
324:
325: # Create a test database. Page 2 is the root page of table t1. The only
326: # row inserted into t1 has an overflow page - page 3. Page 3 will be
327: # coresident on the 2048 byte sector with the next page to be allocated.
328: #
329: db eval { PRAGMA page_size = 1024 }
330: db eval { CREATE TABLE t1(x) }
331: db eval { INSERT INTO t1 VALUES(randomblob(1100)); }
332: } -tclbody {
333: db eval { INSERT INTO t1 VALUES(randomblob(2000)); }
334: }
335: sqlite3_simulate_device -char {} -sectorsize 0
336: catch {db close}
337:
338: do_ioerr_test ioerr-13 -ckrefcount true -erc 1 -sqlprep {
339: PRAGMA auto_vacuum = incremental;
340: CREATE TABLE t1(x);
341: CREATE TABLE t2(x);
342: INSERT INTO t2 VALUES(randomblob(1500));
343: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
344: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
345: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
346: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
347: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
348: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
349: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
350: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
351: INSERT INTO t1 VALUES(randomblob(20));
352: INSERT INTO t1 SELECT x FROM t1;
353: INSERT INTO t1 SELECT x FROM t1;
354: INSERT INTO t1 SELECT x FROM t1;
355: INSERT INTO t1 SELECT x FROM t1;
356: INSERT INTO t1 SELECT x FROM t1;
357: INSERT INTO t1 SELECT x FROM t1; /* 64 entries in t1 */
358: INSERT INTO t1 SELECT x FROM t1 LIMIT 14; /* 78 entries in t1 */
359: DELETE FROM t2 WHERE rowid = 3;
360: } -sqlbody {
361: -- This statement uses the balance_quick() optimization. The new page
362: -- is appended to the database file. But the overflow page used by
363: -- the new record will be positioned near the start of the database
364: -- file, in the gap left by the "DELETE FROM t2 WHERE rowid=3" statement
365: -- above.
366: --
367: -- The point of this is that the statement wil need to update two pointer
368: -- map pages. Which introduces another opportunity for an IO error.
369: --
370: INSERT INTO t1 VALUES(randomblob(2000));
371: }
372:
373: do_ioerr_test ioerr-14 -ckrefcount true -erc 1 -sqlprep {
374: PRAGMA auto_vacuum = incremental;
375: CREATE TABLE t1(x);
376: CREATE TABLE t2(x);
377: INSERT INTO t2 VALUES(randomblob(1500));
378: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
379: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
380: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
381: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
382: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
383: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
384: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
385: INSERT INTO t2 SELECT randomblob(1500) FROM t2;
386:
387: -- This statement inserts a row into t1 with an overflow page at the
388: -- end of the file. A long way from its parent (the root of t1).
389: INSERT INTO t1 VALUES(randomblob(1500));
390: DELETE FROM t2 WHERE rowid<10;
391: } -sqlbody {
392: -- This transaction will cause the root-page of table t1 to divide
393: -- (by calling balance_deeper()). When it does, the "parent" page of the
394: -- overflow page inserted in the -sqlprep block above will change and
395: -- the corresponding pointer map page be updated. This test case attempts
396: -- to cause an IO error during the pointer map page update.
397: --
398: BEGIN;
399: INSERT INTO t1 VALUES(randomblob(100));
400: INSERT INTO t1 VALUES(randomblob(100));
401: INSERT INTO t1 VALUES(randomblob(100));
402: INSERT INTO t1 VALUES(randomblob(100));
403: INSERT INTO t1 VALUES(randomblob(100));
404: INSERT INTO t1 VALUES(randomblob(100));
405: INSERT INTO t1 VALUES(randomblob(100));
406: INSERT INTO t1 VALUES(randomblob(100));
407: INSERT INTO t1 VALUES(randomblob(100));
408: INSERT INTO t1 VALUES(randomblob(100));
409: COMMIT;
410: }
411:
412: do_ioerr_test ioerr-15 -tclprep {
413: db eval {
414: BEGIN;
415: PRAGMA cache_size = 10;
416: CREATE TABLE t1(a);
417: CREATE INDEX i1 ON t1(a);
418: CREATE TABLE t2(a);
419: }
420: for {set ii 1} {$ii < 100} {incr ii} {
421: set v [string range [string repeat [format %.3d $ii] 200] 0 220]
422: db eval {INSERT INTO t1 VALUES($v)}
423: }
424: db eval {
425: DELETE FROM t1 WHERE oid > 85;
426: COMMIT;
427: }
428: } -sqlbody {
429: BEGIN;
430: INSERT INTO t2 VALUES(randstr(22000,22000));
431: DELETE FROM t1 WHERE oid = 83;
432: COMMIT;
433: }
434:
435: # This test verifies that IO errors that occur within the obscure branch
436: # of code executed by tkt3762.test are correctly reported.
437: #
438: ifcapable vacuum&&autovacuum&&pragma {
439: do_ioerr_test ioerr-16 -erc 1 -ckrefcount 1 -sqlprep {
440: PRAGMA auto_vacuum=INCREMENTAL;
441: PRAGMA page_size=1024;
442: BEGIN;
443: CREATE TABLE t1(x);
444: INSERT INTO t1 VALUES(zeroblob(900));
445: INSERT INTO t1 VALUES(zeroblob(900));
446: INSERT INTO t1 SELECT x FROM t1;
447: INSERT INTO t1 SELECT x FROM t1;
448: INSERT INTO t1 SELECT x FROM t1;
449: INSERT INTO t1 SELECT x FROM t1;
450: INSERT INTO t1 SELECT x FROM t1;
451: INSERT INTO t1 SELECT x FROM t1;
452: INSERT INTO t1 SELECT x FROM t1;
453: DELETE FROM t1 WHERE rowid>202;
454: COMMIT;
455: VACUUM;
456: PRAGMA cache_size = 10;
457: BEGIN;
458: DELETE FROM t1 WHERE rowid IN (10,11,12) ;
459: } -sqlbody {
460: PRAGMA incremental_vacuum(10);
461: COMMIT;
462: }
463: }
464:
465: finish_test
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>