xref: /sqlite-3.40.0/test/indexfault.test (revision 480c572f)
1# 2011 August 08
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
18ifcapable !mergesort {
19  finish_test
20  return
21}
22
23set testprefix indexfault
24
25# Set up the custom fault-injector. This is further configured by using
26# different values for $::custom_filter and different implementations
27# of Tcl proc [xCustom] for each test case.
28#
29proc install_custom_faultsim {} {
30  set ::FAULTSIM(custom)            [list      \
31    -injectinstall   custom_injectinstall    \
32    -injectstart     custom_injectstart      \
33    -injectstop      custom_injectstop       \
34    -injecterrlist   {{1 {disk I/O error}}}  \
35    -injectuninstall custom_injectuninstall  \
36  ]
37  proc custom_injectinstall {} {
38    testvfs shmfault -default true
39    shmfault filter $::custom_filter
40    shmfault script xCustom
41  }
42  proc custom_injectuninstall {} {
43    catch {db  close}
44    catch {db2 close}
45    shmfault delete
46  }
47  set ::custom_ifail -1
48  set ::custom_nfail -1
49  proc custom_injectstart {iFail} {
50    set ::custom_ifail $iFail
51    set ::custom_nfail 0
52  }
53  proc custom_injectstop {} {
54    set ::custom_ifail -1
55    return $::custom_nfail
56  }
57}
58proc uninstall_custom_faultsim {} {
59  unset -nocomplain ::FAULTSIM(custom)
60}
61
62
63#-------------------------------------------------------------------------
64# These tests - indexfault-1.* - Build an index on a smallish table with
65# all different kinds of fault-injection. The CREATE INDEX is run once
66# with default options and once with a 50KB soft-heap-limit.
67#
68do_execsql_test 1.0 {
69  BEGIN;
70    CREATE TABLE t1(x);
71    INSERT INTO t1 VALUES(randomblob(202));
72    INSERT INTO t1 SELECT randomblob(202) FROM t1;     --     2
73    INSERT INTO t1 SELECT randomblob(202) FROM t1;     --     4
74    INSERT INTO t1 SELECT randomblob(202) FROM t1;     --     8
75    INSERT INTO t1 SELECT randomblob(202) FROM t1;     --    16
76    INSERT INTO t1 SELECT randomblob(202) FROM t1;     --    32
77    INSERT INTO t1 SELECT randomblob(202) FROM t1;     --    64
78    INSERT INTO t1 SELECT randomblob(202) FROM t1;     --   128
79    INSERT INTO t1 SELECT randomblob(202) FROM t1;     --   256
80  COMMIT;
81}
82faultsim_save_and_close
83
84do_faultsim_test 1.1 -prep {
85  faultsim_restore_and_reopen
86} -body {
87  execsql { CREATE INDEX i1 ON t1(x) }
88  faultsim_test_result {0 {}}
89  faultsim_integrity_check
90}
91ifcapable memorymanage {
92  set soft_limit [sqlite3_soft_heap_limit 50000]
93  do_faultsim_test 2.1 -prep {
94    faultsim_restore_and_reopen
95  } -body {
96    execsql { CREATE INDEX i1 ON t1(x) }
97    faultsim_test_result {0 {}}
98  }
99  sqlite3_soft_heap_limit $soft_limit
100}
101
102#-------------------------------------------------------------------------
103# These are similar to the indexfault-1.* tests, except they create an
104# index with more than one column.
105#
106sqlite3 db test.db
107do_execsql_test 2.0 {
108  BEGIN;
109    DROP TABLE IF EXISTS t1;
110    CREATE TABLE t1(t,u,v,w,x,y,z);
111    INSERT INTO t1 VALUES(
112      randomblob(30), randomblob(30), randomblob(30), randomblob(30),
113      randomblob(30), randomblob(30), randomblob(30)
114    );
115    INSERT INTO t1 SELECT
116      randomblob(30), randomblob(30), randomblob(30), randomblob(30),
117      randomblob(30), randomblob(30), randomblob(30) FROM t1;         -- 2
118    INSERT INTO t1 SELECT
119      randomblob(30), randomblob(30), randomblob(30), randomblob(30),
120      randomblob(30), randomblob(30), randomblob(30) FROM t1;         -- 4
121    INSERT INTO t1 SELECT
122      randomblob(30), randomblob(30), randomblob(30), randomblob(30),
123      randomblob(30), randomblob(30), randomblob(30) FROM t1;         -- 8
124    INSERT INTO t1 SELECT
125      randomblob(30), randomblob(30), randomblob(30), randomblob(30),
126      randomblob(30), randomblob(30), randomblob(30) FROM t1;         -- 16
127    INSERT INTO t1 SELECT
128      randomblob(30), randomblob(30), randomblob(30), randomblob(30),
129      randomblob(30), randomblob(30), randomblob(30) FROM t1;         -- 32
130    INSERT INTO t1 SELECT
131      randomblob(30), randomblob(30), randomblob(30), randomblob(30),
132      randomblob(30), randomblob(30), randomblob(30) FROM t1;         -- 64
133    INSERT INTO t1 SELECT
134      randomblob(30), randomblob(30), randomblob(30), randomblob(30),
135      randomblob(30), randomblob(30), randomblob(30) FROM t1;         -- 128
136  COMMIT;
137}
138faultsim_save_and_close
139
140do_faultsim_test 2.1 -prep {
141  faultsim_restore_and_reopen
142} -body {
143  execsql { CREATE INDEX i1 ON t1(t,u,v,w,x,y,z) }
144  faultsim_test_result {0 {}}
145  faultsim_integrity_check
146}
147ifcapable memorymanage {
148  set soft_limit [sqlite3_soft_heap_limit 50000]
149  do_faultsim_test 2.2 -prep {
150    faultsim_restore_and_reopen
151  } -body {
152    execsql { CREATE INDEX i1 ON t1(t,u,v,w,x,y,z) }
153    faultsim_test_result {0 {}}
154  }
155  sqlite3_soft_heap_limit $soft_limit
156}
157
158#-------------------------------------------------------------------------
159# The following tests - indexfault-2.* - all attempt to build a index
160# on table t1 in the main database with injected IO errors. Individual
161# test cases work as follows:
162#
163#   3.1: IO errors injected into xOpen() calls.
164#   3.2: As 7.1, but with a low (50KB) soft-heap-limit.
165#
166#   3.3: IO errors injected into the first 200 write() calls made on the
167#        second temporary file.
168#   3.4: As 7.3, but with a low (50KB) soft-heap-limit.
169#
170#   3.5: After a certain amount of data has been read from the main database
171#        file (and written into the temporary b-tree), sqlite3_release_memory()
172#        is called to free as much memory as possible. This causes the temp
173#        b-tree to be flushed to disk. So that before its contents can be
174#        transfered to a PMA they must be read back from disk - creating extra
175#        opportunities for IO errors.
176#
177install_custom_faultsim
178
179# Set up a table to build indexes on. Save the setup using the
180# [faultsim_save_and_close] mechanism.
181#
182sqlite3 db test.db
183do_execsql_test 3.0 {
184  BEGIN;
185    DROP TABLE IF EXISTS t1;
186    CREATE TABLE t1(x);
187    INSERT INTO t1 VALUES(randomblob(11000));
188    INSERT INTO t1 SELECT randomblob(11001) FROM t1;     --     2
189    INSERT INTO t1 SELECT randomblob(11002) FROM t1;     --     4
190    INSERT INTO t1 SELECT randomblob(11003) FROM t1;     --     8
191    INSERT INTO t1 SELECT randomblob(11004) FROM t1;     --    16
192    INSERT INTO t1 SELECT randomblob(11005) FROM t1;     --    32
193    INSERT INTO t1 SELECT randomblob(11006) FROM t1;     --    64
194    INSERT INTO t1 SELECT randomblob(11007) FROM t1;     --   128
195    INSERT INTO t1 SELECT randomblob(11008) FROM t1;     --   256
196    INSERT INTO t1 SELECT randomblob(11009) FROM t1;     --   512
197  COMMIT;
198}
199faultsim_save_and_close
200
201set ::custom_filter xOpen
202proc xCustom {args} {
203  incr ::custom_ifail -1
204  if {$::custom_ifail==0} {
205    incr ::custom_nfail
206    return "SQLITE_IOERR"
207  }
208  return "SQLITE_OK"
209}
210do_faultsim_test 3.1 -faults custom -prep {
211  faultsim_restore_and_reopen
212} -body {
213  execsql { CREATE INDEX i1 ON t1(x) }
214  faultsim_test_result {0 {}}
215}
216ifcapable memorymanage {
217  set soft_limit [sqlite3_soft_heap_limit 50000]
218  do_faultsim_test 3.2 -faults custom -prep {
219    faultsim_restore_and_reopen
220  } -body {
221    execsql { CREATE INDEX i1 ON t1(x) }
222    faultsim_test_result {0 {}}
223  }
224  sqlite3_soft_heap_limit $soft_limit
225}
226
227set ::custom_filter {xOpen xWrite}
228proc xCustom {method args} {
229  if {$method == "xOpen"} {
230    if {[lindex $args 0] == ""} {
231      incr ::nTmpOpen 1
232      if {$::nTmpOpen == 3} { return "failme" }
233    }
234    return "SQLITE_OK"
235  }
236  if {$::custom_ifail<200 && [lindex $args 1] == "failme"} {
237    incr ::custom_ifail -1
238    if {$::custom_ifail==0} {
239      incr ::custom_nfail
240      return "SQLITE_IOERR"
241    }
242  }
243  return "SQLITE_OK"
244}
245
246do_faultsim_test 3.3 -faults custom -prep {
247  faultsim_restore_and_reopen
248  set ::nTmpOpen 0
249} -body {
250  execsql { CREATE INDEX i1 ON t1(x) }
251  faultsim_test_result {0 {}}
252}
253
254ifcapable memorymanage {
255  set soft_limit [sqlite3_soft_heap_limit 50000]
256  do_faultsim_test 3.4 -faults custom -prep {
257    faultsim_restore_and_reopen
258    set ::nTmpOpen 0
259  } -body {
260    execsql { CREATE INDEX i1 ON t1(x) }
261    faultsim_test_result {0 {}}
262  }
263  sqlite3_soft_heap_limit $soft_limit
264}
265
266uninstall_custom_faultsim
267
268#-------------------------------------------------------------------------
269# Test 4: After a certain amount of data has been read from the main database
270# file (and written into the temporary b-tree), sqlite3_release_memory() is
271# called to free as much memory as possible. This causes the temp b-tree to be
272# flushed to disk. So that before its contents can be transfered to a PMA they
273# must be read back from disk - creating extra opportunities for IO errors.
274#
275install_custom_faultsim
276
277catch { db close }
278forcedelete test.db
279sqlite3 db test.db
280
281do_execsql_test 4.0 {
282  BEGIN;
283    DROP TABLE IF EXISTS t1;
284    CREATE TABLE t1(x);
285    INSERT INTO t1 VALUES(randomblob(11000));
286    INSERT INTO t1 SELECT randomblob(11001) FROM t1;     --     2
287    INSERT INTO t1 SELECT randomblob(11002) FROM t1;     --     4
288    INSERT INTO t1 SELECT randomblob(11003) FROM t1;     --     8
289    INSERT INTO t1 SELECT randomblob(11004) FROM t1;     --    16
290    INSERT INTO t1 SELECT randomblob(11005) FROM t1;     --    32
291    INSERT INTO t1 SELECT randomblob(11005) FROM t1;     --    64
292  COMMIT;
293}
294faultsim_save_and_close
295
296testvfs tvfs
297tvfs script xRead
298tvfs filter xRead
299set ::nRead 0
300proc xRead {method file args} {
301  if {[file tail $file] == "test.db"} { incr ::nRead }
302}
303
304do_test 4.1 {
305  sqlite3 db test.db -vfs tvfs
306  execsql { CREATE INDEX i1 ON t1(x) }
307} {}
308
309db close
310tvfs delete
311
312set ::custom_filter xRead
313proc xCustom {method file args} {
314  incr ::nReadCall
315  if {$::nReadCall >= ($::nRead/5)} {
316    if {$::nReadCall == ($::nRead/5)} {
317      set nByte [sqlite3_release_memory [expr 64*1024*1024]]
318      sqlite3_soft_heap_limit 20000
319    }
320    if {$file == ""} {
321      incr ::custom_ifail -1
322      if {$::custom_ifail==0} {
323        incr ::custom_nfail
324        return "SQLITE_IOERR"
325      }
326    }
327  }
328  return "SQLITE_OK"
329}
330
331do_faultsim_test 4.2 -faults custom -prep {
332  faultsim_restore_and_reopen
333  set ::nReadCall 0
334  sqlite3_soft_heap_limit 0
335} -body {
336  execsql { CREATE INDEX i1 ON t1(x) }
337  faultsim_test_result {0 {}}
338}
339
340do_faultsim_test 5 -prep {
341  reset_db
342} -body {
343  execsql {
344 CREATE TABLE reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongname(a PRIMARY KEY) WITHOUT ROWID;
345  }
346} -test {
347  faultsim_test_result {0 {}}
348}
349
350uninstall_custom_faultsim
351
352finish_test
353