blob: d6a01fec85dd3e6823d95804daa203372afee3a4 [file] [log] [blame]
danb0ac3e32010-06-16 10:55:42 +00001# 2010 June 15
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
13set testdir [file dirname $argv0]
14source $testdir/tester.tcl
15source $testdir/lock_common.tcl
16source $testdir/malloc_common.tcl
17
18set a_string_counter 1
19proc a_string {n} {
20 global a_string_counter
21 incr a_string_counter
22 string range [string repeat "${a_string_counter}." $n] 1 $n
23}
24db func a_string a_string
25
26#-------------------------------------------------------------------------
27# Test fault-injection while rolling back a hot-journal file.
28#
29do_test pagerfault-1-pre1 {
30 execsql {
31 PRAGMA journal_mode = DELETE;
32 PRAGMA cache_size = 10;
33 CREATE TABLE t1(a UNIQUE, b UNIQUE);
34 INSERT INTO t1 VALUES(a_string(200), a_string(300));
35 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
36 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
37 BEGIN;
38 INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
39 INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
40 INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
41 INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
42 }
43 faultsim_save_and_close
44} {}
45do_faultsim_test pagerfault-1 -prep {
46 faultsim_restore_and_reopen
47} -body {
48 execsql { SELECT count(*) FROM t1 }
49} -test {
50 faultsim_test_result {0 4}
51 faultsim_integrity_check
52 if {[db one { SELECT count(*) FROM t1 }] != 4} {
53 error "Database content appears incorrect"
54 }
55}
56
57#-------------------------------------------------------------------------
dande4996e2010-06-19 11:30:41 +000058# Test fault-injection while rolling back a hot-journal file with a
59# page-size different from the current value stored on page 1 of the
60# database file.
61#
62do_test pagerfault-2-pre1 {
63 testvfs tv -default 1
64 tv filter xSync
65 tv script xSyncCb
66 proc xSyncCb {filename args} {
67 if {[string match *journal filename]==0} faultsim_save
68 }
69 faultsim_delete_and_reopen
70 execsql {
71 PRAGMA page_size = 4096;
72 BEGIN;
73 CREATE TABLE abc(a, b, c);
74 INSERT INTO abc VALUES('o', 't', 't');
75 INSERT INTO abc VALUES('f', 'f', 's');
76 INSERT INTO abc SELECT * FROM abc; -- 4
77 INSERT INTO abc SELECT * FROM abc; -- 8
78 INSERT INTO abc SELECT * FROM abc; -- 16
79 INSERT INTO abc SELECT * FROM abc; -- 32
80 INSERT INTO abc SELECT * FROM abc; -- 64
81 INSERT INTO abc SELECT * FROM abc; -- 128
82 INSERT INTO abc SELECT * FROM abc; -- 256
83 COMMIT;
84 PRAGMA page_size = 1024;
85 VACUUM;
86 }
87 db close
88 tv delete
89} {}
90do_faultsim_test pagerfault-2 -prep {
91 faultsim_restore_and_reopen
92} -body {
93 execsql { SELECT * FROM abc }
94} -test {
95 set answer [split [string repeat "ottffs" 128] ""]
96 faultsim_test_result [list 0 $answer]
97 faultsim_integrity_check
98 set res [db eval { SELECT * FROM abc }]
99 if {$res != $answer} { error "Database content appears incorrect ($res)" }
danec6ffc12010-06-24 19:16:06 +0000100}
dande4996e2010-06-19 11:30:41 +0000101
102#-------------------------------------------------------------------------
danb0ac3e32010-06-16 10:55:42 +0000103# Test fault-injection while rolling back hot-journals that were created
104# as part of a multi-file transaction.
105#
dande4996e2010-06-19 11:30:41 +0000106do_test pagerfault-3-pre1 {
danb0ac3e32010-06-16 10:55:42 +0000107 testvfs tstvfs -default 1
108 tstvfs filter xDelete
109 tstvfs script xDeleteCallback
110
111 proc xDeleteCallback {method file args} {
112 set file [file tail $file]
113 if { [string match *mj* $file] } { faultsim_save }
114 }
115
116 faultsim_delete_and_reopen
117 db func a_string a_string
118
119 execsql {
120 ATTACH 'test.db2' AS aux;
121 PRAGMA journal_mode = DELETE;
122 PRAGMA main.cache_size = 10;
123 PRAGMA aux.cache_size = 10;
124
125 CREATE TABLE t1(a UNIQUE, b UNIQUE);
126 CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
127 INSERT INTO t1 VALUES(a_string(200), a_string(300));
128 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
129 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
130 INSERT INTO t2 SELECT * FROM t1;
131
132 BEGIN;
133 INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
134 INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
135 INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
136 INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
137 REPLACE INTO t2 SELECT * FROM t1;
138 COMMIT;
139 }
140
141 db close
142 tstvfs delete
143} {}
danec6ffc12010-06-24 19:16:06 +0000144do_faultsim_test pagerfault-3 -prep {
danb0ac3e32010-06-16 10:55:42 +0000145 faultsim_restore_and_reopen
146} -body {
147 execsql {
148 ATTACH 'test.db2' AS aux;
149 SELECT count(*) FROM t2;
150 SELECT count(*) FROM t1;
151 }
152} -test {
153 faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}}
154 faultsim_integrity_check
danb0ac3e32010-06-16 10:55:42 +0000155 catchsql { ATTACH 'test.db2' AS aux }
156 if {[db one { SELECT count(*) FROM t1 }] != 4
157 || [db one { SELECT count(*) FROM t2 }] != 4
158 } {
159 error "Database content appears incorrect"
160 }
161}
162
dande4996e2010-06-19 11:30:41 +0000163#-------------------------------------------------------------------------
164# Test fault-injection as part of a vanilla, no-transaction, INSERT
165# statement.
166#
167do_faultsim_test pagerfault-4 -prep {
168 faultsim_delete_and_reopen
169} -body {
170 execsql {
171 CREATE TABLE x(y);
172 INSERT INTO x VALUES('z');
173 SELECT * FROM x;
174 }
175} -test {
176 faultsim_test_result {0 z}
177 faultsim_integrity_check
178}
179
180#-------------------------------------------------------------------------
181# Test fault-injection as part of a commit when using journal_mode=PERSIST.
dan146ed782010-06-19 17:26:37 +0000182# Three different cases:
183#
184# pagerfault-5.1: With no journal_size_limit configured.
185# pagerfault-5.2: With a journal_size_limit configured.
186# pagerfault-5.4: Multi-file transaction. One connection has a
187# journal_size_limit of 0, the other has no limit.
dande4996e2010-06-19 11:30:41 +0000188#
189do_test pagerfault-5-pre1 {
190 faultsim_delete_and_reopen
191 db func a_string a_string
192 execsql {
193 CREATE TABLE t1(a UNIQUE, b UNIQUE);
194 INSERT INTO t1 VALUES(a_string(200), a_string(300));
195 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
196 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
197 }
198 faultsim_save_and_close
199} {}
200do_faultsim_test pagerfault-5.1 -prep {
201 faultsim_restore_and_reopen
202 db func a_string a_string
203 execsql { PRAGMA journal_mode = PERSIST }
204} -body {
205 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
206} -test {
207 faultsim_test_result {0 {}}
208 faultsim_integrity_check
209}
210do_faultsim_test pagerfault-5.2 -prep {
211 faultsim_restore_and_reopen
212 db func a_string a_string
213 execsql {
214 PRAGMA journal_mode = PERSIST;
215 PRAGMA journal_size_limit = 2048;
216 }
217} -body {
218 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
219} -test {
220 faultsim_test_result {0 {}}
221 faultsim_integrity_check
222}
danec6ffc12010-06-24 19:16:06 +0000223do_faultsim_test pagerfault-5.3 -faults oom-transient -prep {
dande4996e2010-06-19 11:30:41 +0000224 faultsim_restore_and_reopen
225 db func a_string a_string
226 file delete -force test2.db test2.db-journal test2.db-wal
227 execsql {
228 PRAGMA journal_mode = PERSIST;
229 ATTACH 'test2.db' AS aux;
230 PRAGMA aux.journal_mode = PERSIST;
231 PRAGMA aux.journal_size_limit = 0;
232 }
233} -body {
234 execsql {
235 BEGIN;
236 INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
237 CREATE TABLE aux.t2 AS SELECT * FROM t1;
238 COMMIT;
239 }
240} -test {
241 faultsim_test_result {0 {}}
danec6ffc12010-06-24 19:16:06 +0000242 faultsim_integrity_check
243
244 set res ""
245 set rc [catch { set res [db one { PRAGMA aux.integrity_check }] }]
246 if {$rc!=0 || $res != "ok"} {error "integrity-check problem:$rc $res"}
dande4996e2010-06-19 11:30:41 +0000247}
248
dan153eda02010-06-21 07:45:47 +0000249#-------------------------------------------------------------------------
250# Test fault-injection as part of a commit when using
251# journal_mode=TRUNCATE.
252#
253do_test pagerfault-6-pre1 {
254 faultsim_delete_and_reopen
255 db func a_string a_string
256 execsql {
257 CREATE TABLE t1(a UNIQUE, b UNIQUE);
258 INSERT INTO t1 VALUES(a_string(200), a_string(300));
259 }
260 faultsim_save_and_close
261} {}
danf9b44192010-06-25 19:09:48 +0000262
dan153eda02010-06-21 07:45:47 +0000263do_faultsim_test pagerfault-6.1 -prep {
264 faultsim_restore_and_reopen
265 db func a_string a_string
266 execsql { PRAGMA journal_mode = TRUNCATE }
267} -body {
268 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
danf9b44192010-06-25 19:09:48 +0000269 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
270} -test {
271 faultsim_test_result {0 {}}
272 faultsim_integrity_check
273}
274
275# The unix vfs xAccess() method considers a file zero bytes in size to
276# "not exist". This proc overrides that behaviour so that a zero length
277# file is considered to exist.
278#
279proc xAccess {method filename op args} {
280 if {$op != "SQLITE_ACCESS_EXISTS"} { return "" }
281 return [file exists $filename]
282}
283do_faultsim_test pagerfault-6.2 -faults cantopen-* -prep {
284 shmfault filter xAccess
285 shmfault script xAccess
286
287 faultsim_restore_and_reopen
288 db func a_string a_string
289 execsql { PRAGMA journal_mode = TRUNCATE }
290} -body {
291 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
292 execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
dan153eda02010-06-21 07:45:47 +0000293} -test {
294 faultsim_test_result {0 {}}
295 faultsim_integrity_check
296}
297
dan146ed782010-06-19 17:26:37 +0000298# The following was an attempt to get a bitvec malloc to fail. Didn't work.
299#
300# do_test pagerfault-6-pre1 {
301# faultsim_delete_and_reopen
302# execsql {
303# CREATE TABLE t1(x, y, UNIQUE(x, y));
304# INSERT INTO t1 VALUES(1, randomblob(1501));
305# INSERT INTO t1 VALUES(2, randomblob(1502));
306# INSERT INTO t1 VALUES(3, randomblob(1503));
307# INSERT INTO t1 VALUES(4, randomblob(1504));
308# INSERT INTO t1
309# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
310# INSERT INTO t1
311# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
312# INSERT INTO t1
313# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
314# INSERT INTO t1
315# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
316# }
317# faultsim_save_and_close
318# } {}
319# do_faultsim_test pagerfault-6 -prep {
320# faultsim_restore_and_reopen
321# } -body {
322# execsql {
323# BEGIN;
324# UPDATE t1 SET x=x+4 WHERE x=1;
325# SAVEPOINT one;
326# UPDATE t1 SET x=x+4 WHERE x=2;
327# SAVEPOINT three;
328# UPDATE t1 SET x=x+4 WHERE x=3;
329# SAVEPOINT four;
330# UPDATE t1 SET x=x+4 WHERE x=4;
331# RELEASE three;
332# COMMIT;
333# SELECT DISTINCT x FROM t1;
334# }
335# } -test {
336# faultsim_test_result {0 {5 6 7 8}}
337# faultsim_integrity_check
338# }
dan346e4262010-06-23 19:27:36 +0000339#
dandca321a2010-06-24 10:50:17 +0000340
341# This is designed to provoke a special case in the pager code:
342#
343# If an error (specifically, a FULL or IOERR error) occurs while writing a
344# dirty page to the file-system in order to free up memory, the pager enters
345# the "error state". An IO error causes SQLite to roll back the current
346# transaction (exiting the error state). A FULL error, however, may only
347# rollback the current statement.
348#
349# This block tests that nothing goes wrong if a FULL error occurs while
350# writing a dirty page out to free memory from within a statement that has
351# opened a statement transaction.
352#
dan346e4262010-06-23 19:27:36 +0000353do_test pagerfault-7-pre1 {
354 faultsim_delete_and_reopen
355 execsql {
356 CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
357 BEGIN;
358 INSERT INTO t2 VALUES(NULL, randomblob(1500));
359 INSERT INTO t2 VALUES(NULL, randomblob(1500));
360 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 4
361 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 8
362 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 16
363 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 32
364 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 64
365 COMMIT;
366 CREATE TABLE t1(a PRIMARY KEY, b);
367 INSERT INTO t1 SELECT * FROM t2;
368 DROP TABLE t2;
369 }
370 faultsim_save_and_close
371} {}
danec6ffc12010-06-24 19:16:06 +0000372do_faultsim_test pagerfault-7 -prep {
dan346e4262010-06-23 19:27:36 +0000373 faultsim_restore_and_reopen
374 execsql {
375 PRAGMA cache_size = 10;
376 BEGIN;
377 UPDATE t1 SET b = randomblob(1500);
378 }
379} -body {
380 execsql { UPDATE t1 SET a = 65, b = randomblob(1500) WHERE (a+1)>200 }
381 execsql COMMIT
382} -test {
383 faultsim_test_result {0 {}}
384 faultsim_integrity_check
385}
dan146ed782010-06-19 17:26:37 +0000386
dandca321a2010-06-24 10:50:17 +0000387do_test pagerfault-8-pre1 {
388 faultsim_delete_and_reopen
389 execsql {
390 PRAGMA auto_vacuum = 1;
391 CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
392 BEGIN;
393 INSERT INTO t1 VALUES(NULL, randomblob(1500));
394 INSERT INTO t1 VALUES(NULL, randomblob(1500));
395 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 4
396 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 8
397 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 16
398 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 32
399 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 64
400 COMMIT;
401 }
402 faultsim_save_and_close
403 set filesize [file size test.db]
404 set {} {}
405} {}
406do_test pagerfault-8-pre2 {
407 faultsim_restore_and_reopen
408 execsql { DELETE FROM t1 WHERE a>32 }
409 expr {[file size test.db] < $filesize}
410} {1}
dandca321a2010-06-24 10:50:17 +0000411do_faultsim_test pagerfault-8 -prep {
412 faultsim_restore_and_reopen
413 execsql {
414 BEGIN;
415 DELETE FROM t1 WHERE a>32;
416 }
417} -body {
418 execsql COMMIT
419} -test {
420 faultsim_test_result {0 {}}
421 faultsim_integrity_check
422}
423
dan0a6052e2010-06-24 13:24:26 +0000424do_test pagerfault-9-pre1 {
425 faultsim_delete_and_reopen
426 execsql {
427 PRAGMA auto_vacuum = incremental;
428 CREATE TABLE t1(x);
429 CREATE TABLE t2(y);
430 CREATE TABLE t3(z);
431
432 INSERT INTO t1 VALUES(randomblob(900));
433 INSERT INTO t1 VALUES(randomblob(900));
434 DELETE FROM t1;
435 }
436 faultsim_save_and_close
437} {}
438
danec6ffc12010-06-24 19:16:06 +0000439do_faultsim_test pagerfault-9 -prep {
dan0a6052e2010-06-24 13:24:26 +0000440 faultsim_restore_and_reopen
441 execsql {
442 BEGIN;
443 INSERT INTO t1 VALUES(randomblob(900));
444 INSERT INTO t1 VALUES(randomblob(900));
445 DROP TABLE t3;
446 DROP TABLE t2;
447 SAVEPOINT abc;
448 PRAGMA incremental_vacuum;
449 }
450} -body {
451 execsql {
452 ROLLBACK TO abc;
453 COMMIT;
454 PRAGMA freelist_count
455 }
456} -test {
457 faultsim_test_result {0 2}
458 faultsim_integrity_check
459
460 set sl [db one { SELECT COALESCE(sum(length(x)), 'null') FROM t1 }]
461 if {$sl!="null" && $sl!=1800} {
462 error "Content looks no good... ($sl)"
463 }
464}
465
danb0ac3e32010-06-16 10:55:42 +0000466finish_test