Enhance the command-line completion extension to return the names of
[sqlite.git] / ext / session / sessionfault.test
blobbe6c4568ce6584bc6da7f221c2fcbcfa05d5aca8
1 # 2011 Mar 21
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
12 # The focus of this file is testing the session module.
15 if {![info exists testdir]} {
16   set testdir [file join [file dirname [info script]] .. .. test]
17
18 source [file join [file dirname [info script]] session_common.tcl]
19 source $testdir/tester.tcl
20 ifcapable !session {finish_test; return}
22 set testprefix sessionfault
24 forcedelete test.db2
25 sqlite3 db2 test.db2
26 do_common_sql {
27   CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
28   INSERT INTO t1 VALUES(1, 2, 3);
29   INSERT INTO t1 VALUES(4, 5, 6);
31 faultsim_save_and_close
32 db2 close
34 #-------------------------------------------------------------------------
35 # Test OOM error handling when collecting and applying a simple changeset.
37 # Test 1.1 attaches tables individually by name to the session object. 
38 # Whereas test 1.2 passes NULL to sqlite3session_attach() to attach all
39 # tables.
41 do_faultsim_test 1.1 -faults oom-* -prep {
42   catch {db2 close}
43   catch {db close}
44   faultsim_restore_and_reopen
45   sqlite3 db2 test.db2
46 } -body {
47   do_then_apply_sql {
48     INSERT INTO t1 VALUES('a string value', 8, 9);
49     UPDATE t1 SET c = 10 WHERE a = 1;
50     DELETE FROM t1 WHERE a = 4;
51   }
52 } -test {
53   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
54   faultsim_integrity_check
55   if {$testrc==0} { compare_db db db2 }
58 do_faultsim_test 1.2 -faults oom-* -prep {
59   catch {db2 close}
60   catch {db close}
61   faultsim_restore_and_reopen
62 } -body {
63   sqlite3session S db main
64   S attach *
65   execsql {
66     INSERT INTO t1 VALUES('a string value', 8, 9);
67     UPDATE t1 SET c = 10 WHERE a = 1;
68     DELETE FROM t1 WHERE a = 4;
69   }
70   set ::changeset [S changeset]
71   set {} {}
72 } -test {
73   catch { S delete }
74   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
75   faultsim_integrity_check
76   if {$testrc==0} { 
77     proc xConflict {args} { return "OMIT" }
78     sqlite3 db2 test.db2
79     sqlite3changeset_apply db2 $::changeset xConflict
80     compare_db db db2 
81   }
84 #-------------------------------------------------------------------------
85 # The following block of tests - 2.* - are designed to check 
86 # the handling of faults in the sqlite3changeset_apply() function.
88 catch {db close}
89 catch {db2 close}
90 forcedelete test.db2 test.db
91 sqlite3 db2 test.db2
92 sqlite3 db test.db
93 do_common_sql {
94   CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
95   INSERT INTO t1 VALUES('apple', 'orange', 'pear');
97   CREATE TABLE t2(x PRIMARY KEY, y);
99 db2 close
100 faultsim_save_and_close
103 foreach {tn conflict_policy sql sql2} {
104   1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {}
105   2 OMIT { DELETE FROM t1 WHERE a = 'apple' }                         {}
106   3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' }            {}
107   4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } {
108     INSERT INTO t2 VALUES('keyvalue', 'value 2');
109   }
110 } {
111   proc xConflict args [list return $conflict_policy]
113   do_faultsim_test 2.$tn -faults oom-transient -prep {
114     catch {db2 close}
115     catch {db close}
116     faultsim_restore_and_reopen
117     set ::changeset [changeset_from_sql $::sql]
118     sqlite3 db2 test.db2
119     sqlite3_db_config_lookaside db2 0 0 0
120     execsql $::sql2 db2
121   } -body {
122     sqlite3changeset_apply db2 $::changeset xConflict
123   } -test {
124     faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
125     faultsim_integrity_check
126     if {$testrc==0} { compare_db db db2 }
127   }
130 #-------------------------------------------------------------------------
131 # This test case is designed so that a malloc() failure occurs while
132 # resizing the session object hash-table from 256 to 512 buckets. This
133 # is not an error, just a sub-optimal condition.
135 do_faultsim_test 3 -faults oom-* -prep {
136   catch {db2 close}
137   catch {db close}
138   faultsim_restore_and_reopen
139   sqlite3 db2 test.db2
141   sqlite3session S db main
142   S attach t1
143   execsql { BEGIN }
144   for {set i 0} {$i < 125} {incr i} {
145     execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)}
146   }
147 } -body {
148   for {set i 125} {$i < 133} {incr i} {
149     execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)}
150   }
151   S changeset
152   set {} {}
153 } -test {
154   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
155   if {$testrc==0} { 
156     sqlite3changeset_apply db2 [S changeset] xConflict
157     compare_db db db2 
158   }
159   catch { S delete }
160   faultsim_integrity_check
163 catch { db close }
164 catch { db2 close }
165 forcedelete test.db2 test.db
166 sqlite3 db2 test.db2
167 sqlite3 db test.db
169 proc xConflict {op tbl type args} {
170   if { $type=="CONFLICT" || $type=="DATA" } {
171     return "REPLACE"
172   }
173   return "OMIT"
176 do_test 4.0 {
177   execsql {
178     PRAGMA encoding = 'utf16';
179     CREATE TABLE t1(a PRIMARY KEY, b);
180     INSERT INTO t1 VALUES(5, 32);
181   }
182   execsql {
183     PRAGMA encoding = 'utf16';
184     CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
185     INSERT INTO t1 VALUES(1, 2);
186     INSERT INTO t1 VALUES(2, 4);
187     INSERT INTO t1 VALUES(4, 16);
188   } db2
189 } {}
191 faultsim_save_and_close
192 db2 close
194 do_faultsim_test 4 -faults oom-* -prep {
195   catch {db2 close}
196   catch {db close}
197   faultsim_restore_and_reopen
198   sqlite3 db2 test.db2
199   sqlite3session S db main
200   S attach t1
201   execsql {
202     INSERT INTO t1 VALUES(1, 45);
203     INSERT INTO t1 VALUES(2, 55);
204     INSERT INTO t1 VALUES(3, 55);
205     UPDATE t1 SET a = 4 WHERE a = 5;
206   }
207 } -body {
208   sqlite3changeset_apply db2 [S changeset] xConflict
209 } -test {
210   catch { S delete }
211   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
212   if {$testrc==0} { compare_db db db2 }
215 #-------------------------------------------------------------------------
216 # This block of tests verifies that OOM faults in the 
217 # sqlite3changeset_invert() function are handled correctly.
219 catch {db close}
220 catch {db2 close}
221 forcedelete test.db
222 sqlite3 db test.db
223 execsql {
224   CREATE TABLE t1(a, b, PRIMARY KEY(b));
225   CREATE TABLE t2(a PRIMARY KEY, b);
226   INSERT INTO t1 VALUES('string', 1);
227   INSERT INTO t1 VALUES(4, 2);
228   INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
230 set changeset [changeset_from_sql {
231   INSERT INTO t1 VALUES('xxx', 'yyy');
232   DELETE FROM t1 WHERE a = 'string';
233   UPDATE t1 SET a = 20 WHERE b = 2;
235 db close
237 do_faultsim_test 5.1 -faults oom* -body {
238   set ::inverse [sqlite3changeset_invert $::changeset]
239   set {} {}
240 } -test {
241   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
242   if {$testrc==0} {
243     set x [list]
244     sqlite3session_foreach c $::inverse { lappend x $c }
245     foreach c {
246         {DELETE t1 0 .X {t xxx t yyy} {}} 
247         {INSERT t1 0 .X {} {t string i 1}} 
248         {UPDATE t1 0 .X {i 20 i 2} {i 4 {} {}}}
249     } { lappend y $c }
250     if {$x != $y} { error "changeset no good" }
251   }
254 catch {db close}
255 catch {db2 close}
256 forcedelete test.db
257 sqlite3 db test.db
258 execsql {
259   CREATE TABLE t2(a PRIMARY KEY, b);
260   INSERT INTO t2 VALUES(1, 'abc');
261   INSERT INTO t2 VALUES(2, 'def');
263 set changeset [changeset_from_sql {
264   UPDATE t2 SET b = (b || b || b || b);
265   UPDATE t2 SET b = (b || b || b || b);
266   UPDATE t2 SET b = (b || b || b || b);
267   UPDATE t2 SET b = (b || b || b || b);
269 db close
270 set abc [string repeat abc 256]
271 set def [string repeat def 256]
273 do_faultsim_test 5.2 -faults oom-tra* -body {
274   set ::inverse [sqlite3changeset_invert $::changeset]
275   set {} {}
276 } -test {
277   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
278   if {$testrc==0} {
279     set x [list]
280     sqlite3session_foreach c $::inverse { lappend x $c }
281     foreach c "
282         {UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}}
283         {UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}}
284     " { lappend y $c }
285     if {$x != $y} { error "changeset no good" }
286   }
289 catch {db close}
290 catch {db2 close}
291 forcedelete test.db
292 sqlite3 db test.db
293 set abc [string repeat abc 256]
294 set def [string repeat def 256]
295 execsql "
296   CREATE TABLE t2(a PRIMARY KEY, b);
297   INSERT INTO t2 VALUES(1, '$abc');
299 set changeset [changeset_from_sql "
300   INSERT INTO t2 VALUES(2, '$def');
301   DELETE FROM t2 WHERE a = 1;
303 db close
305 do_faultsim_test 5.3 -faults oom-tra* -body {
306   set ::inverse [sqlite3changeset_invert $::changeset]
307   set {} {}
308 } -test {
309   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
310   if {$testrc==0} {
311     set x [list]
312     sqlite3session_foreach c $::inverse { lappend x $c }
313     foreach c "
314         {INSERT t2 0 X. {} {i 1 t $::abc}}
315         {DELETE t2 0 X. {i 2 t $::def} {}}
316     " { lappend y $c }
317     if {$x != $y} { error "changeset no good" }
318   }
321 #-------------------------------------------------------------------------
322 # Test that OOM errors in sqlite3changeset_concat() are handled correctly.
324 catch {db close}
325 forcedelete test.db
326 sqlite3 db test.db
327 do_execsql_test 5.prep1 {
328   CREATE TABLE t1(a, b, PRIMARY KEY(b));
329   CREATE TABLE t2(a PRIMARY KEY, b);
330   INSERT INTO t1 VALUES('string', 1);
331   INSERT INTO t1 VALUES(4, 2);
332   INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
335 do_test 6.prep2 {
336   sqlite3session M db main
337   M attach *
338   set ::c2 [changeset_from_sql {
339     INSERT INTO t2 VALUES(randomblob(1000), randomblob(1000));
340     INSERT INTO t2 VALUES('one', 'two');
341     INSERT INTO t2 VALUES(1, NULL);
342     UPDATE t1 SET a = 5 WHERE a = 2;
343   }]
344   set ::c1 [changeset_from_sql {
345     DELETE FROM t2 WHERE a = 1;
346     UPDATE t1 SET a = 4 WHERE a = 2;
347     INSERT INTO t2 VALUES('x', 'y');
348   }]
349   set ::total [changeset_to_list [M changeset]]
350   M delete
351 } {}
353 do_faultsim_test 6 -faults oom-* -body {
354   set ::result [sqlite3changeset_concat $::c1 $::c2]
355   set {} {}
356 } -test {
357   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
358   if {$testrc==0} {
359     set v [changeset_to_list $::result]
360     if {$v != $::total} { error "result no good" }
361   }
364 faultsim_delete_and_reopen
365 do_execsql_test 7.prep1 {
366   CREATE TABLE t1(a, b, PRIMARY KEY(a));
368 faultsim_save_and_close
370 set res [list]
371 for {set ::i 0} {$::i < 480} {incr ::i 4} {
372   lappend res "INSERT t1 0 X. {} {i $::i i $::i}"
374 set res [lsort $res]
375 do_faultsim_test 7 -faults oom-transient -prep {
376   catch { S delete }
377   faultsim_restore_and_reopen
378   sqlite3session S db main
379   S attach *
380 } -body {
381   for {set ::i 0} {$::i < 480} {incr ::i 4} {
382     execsql {INSERT INTO t1 VALUES($::i, $::i)}
383   }
384 } -test {
385   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
386   if {$testrc==0} {
387     set cres [list [catch {changeset_to_list [S changeset]} msg] $msg]
388     S delete
389     if {$cres != "1 SQLITE_NOMEM" && $cres != "0 {$::res}"} {
390       error "Expected {0 $::res} Got {$cres}"
391     }
392   } else {
393     catch { S changeset }
394     catch { S delete }
395   }
398 faultsim_delete_and_reopen
399 do_test 8.prep {
400   sqlite3session S db main
401   S attach *
402   execsql { 
403     CREATE TABLE t1(a, b, PRIMARY KEY(a)); 
404     INSERT INTO t1 VALUES(1, 2);
405     INSERT INTO t1 VALUES(3, 4);
406     INSERT INTO t1 VALUES(5, 6);
407   }
408   set ::changeset [S changeset]
409   S delete
410 } {}
412 set expected [normalize_list {
413   {INSERT t1 0 X. {} {i 1 i 2}} 
414   {INSERT t1 0 X. {} {i 3 i 4}} 
415   {INSERT t1 0 X. {} {i 5 i 6}}
417 do_faultsim_test 8.1 -faults oom* -body {
418   set ::res [list]
419   sqlite3session_foreach -next v $::changeset { lappend ::res $v }
420   normalize_list $::res
421 } -test {
422   faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
424 do_faultsim_test 8.2 -faults oom* -body {
425   set ::res [list]
426   sqlite3session_foreach v $::changeset { lappend ::res $v }
427   normalize_list $::res
428 } -test {
429   faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
432 faultsim_delete_and_reopen
433 do_test 9.1.prep {
434   execsql { 
435     PRAGMA encoding = 'utf16';
436     CREATE TABLE t1(a PRIMARY KEY, b);
437   }
438 } {}
439 faultsim_save_and_close
441 set answers [list {0 {}} {1 SQLITE_NOMEM} \
442                   {1 {callback requested query abort}} \
443                   {1 {abort due to ROLLBACK}}]
444 do_faultsim_test 9.1 -faults oom-transient -prep {
445   catch { unset ::c }
446   faultsim_restore_and_reopen
447   sqlite3session S db main
448   S attach *
449 } -body {
450   execsql {
451     INSERT INTO t1 VALUES('abcdefghijklmnopqrstuv', 'ABCDEFGHIJKLMNOPQRSTUV');
452   }
453   set ::c [S changeset]
454   set {} {}
455 } -test {
456   S delete
457   eval faultsim_test_result $::answers
458   if {[info exists ::c]} {
459     set expected [normalize_list {
460       {INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}}
461     }]
462     if { [changeset_to_list $::c] != $expected } {
463       error "changeset mismatch"
464     }
465   }
468 faultsim_delete_and_reopen
469 do_test 9.2.prep {
470   execsql { 
471     PRAGMA encoding = 'utf16';
472     CREATE TABLE t1(a PRIMARY KEY, b);
473     INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV');
474   }
475 } {}
476 faultsim_save_and_close
478 set answers [list {0 {}} {1 SQLITE_NOMEM} \
479                   {1 {callback requested query abort}} \
480                   {1 {abort due to ROLLBACK}}]
481 do_faultsim_test 9.2 -faults oom-transient -prep {
482   catch { unset ::c }
483   faultsim_restore_and_reopen
484   sqlite3session S db main
485   S attach *
486 } -body {
487   execsql {
488     UPDATE t1 SET b = 'xyz';
489   }
490   set ::c [S changeset]
491   set {} {}
492 } -test {
493   S delete
494   eval faultsim_test_result $::answers
495   if {[info exists ::c]} {
496     set expected [normalize_list {
497       {UPDATE t1 0 X. {t abcdefghij t ABCDEFGHIJKLMNOPQRSTUV} {{} {} t xyz}}
498     }]
499     if { [changeset_to_list $::c] != $expected } {
500       error "changeset mismatch"
501     }
502   }
505 #-------------------------------------------------------------------------
506 # Test that if a conflict-handler encounters an OOM in 
507 # sqlite3_value_text() but goes on to return SQLITE_CHANGESET_REPLACE
508 # anyway, the OOM is picked up by the sessions module.
509 set bigstr [string repeat abcdefghij 100]
510 faultsim_delete_and_reopen
511 do_test 10.prep.1  {
512   execsql {
513     CREATE TABLE t1(a PRIMARY KEY, b);
514     INSERT INTO t1 VALUES($bigstr, $bigstr);
515   }
517   sqlite3session S db main
518   S attach *
519   execsql { UPDATE t1 SET b = b||'x' }
520   set C [S changeset]
521   S delete
522   execsql { UPDATE t1 SET b = b||'xyz' }
523 } {}
524 faultsim_save_and_close
526 faultsim_restore_and_reopen
527 do_test 10.prep.2  {
528   proc xConflict {args} { return "ABORT" }
529   list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
530 } {1 SQLITE_ABORT}
531 do_execsql_test 10.prep.3 { SELECT b=$bigstr||'x' FROM t1 } 0
532 do_test 10.prep.4  {
533   proc xConflict {args} { return "REPLACE" }
534   list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
535 } {0 {}}
536 do_execsql_test 10.prep.5 { SELECT b=$bigstr||'x' FROM t1 } 1
537 db close
539 do_faultsim_test 10 -faults oom-tra* -prep {
540   faultsim_restore_and_reopen
541 } -body {
542   sqlite3changeset_apply_replace_all db $::C 
543 } -test {
544   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
545   if {$testrc==0} {
546     if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} {
547       error "data does not look right"
548     }
549   }
552 #-------------------------------------------------------------------------
553 # Test an OOM with an sqlite3changeset_apply() filter callback.
555 reset_db
556 do_test 11.prep {
557   execsql {
558     CREATE TABLE t1(a PRIMARY KEY, b);
559     CREATE TABLE t2(x PRIMARY KEY, y);
560     BEGIN;
561   }
563   set ::cs [changeset_from_sql { 
564     INSERT INTO t1 VALUES(1, 2);
565     INSERT INTO t2 VALUES('x', 'y');
566   }]
568   execsql ROLLBACK
569   set {} {}
570 } {}
572 proc filter {x} { return [string equal t1 $x] } 
573 faultsim_save_and_close
575 do_faultsim_test 11 -faults oom-tra* -prep {
576   faultsim_restore_and_reopen
577 } -body {
578   sqlite3changeset_apply db $::cs {} filter
579 } -test {
580   faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
581   if {$testrc==0} {
582     if {[db eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2}] != "1 2"} {
583       error "data does not look right"
584     }
585   }
589 finish_test