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]
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
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
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
41 do_faultsim_test 1.1 -faults oom-* -prep {
44 faultsim_restore_and_reopen
47 do_then_apply_sql -ignorenoop {
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;
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 {
61 faultsim_restore_and_reopen
63 sqlite3session S db main
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;
70 set ::changeset [S changeset]
74 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
75 faultsim_integrity_check
77 proc xConflict {args} { return "OMIT" }
79 sqlite3changeset_apply db2 $::changeset xConflict
84 #-------------------------------------------------------------------------
85 # The following block of tests - 2.* - are designed to check
86 # the handling of faults in the sqlite3changeset_apply() function.
90 forcedelete test.db2 test.db
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);
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');
111 proc xConflict args [list return $conflict_policy]
113 do_faultsim_test 2.$tn -faults oom-transient -prep {
116 faultsim_restore_and_reopen
117 set ::changeset [changeset_from_sql $::sql]
119 sqlite3_db_config_lookaside db2 0 0 0
122 sqlite3changeset_apply db2 $::changeset xConflict
124 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
125 faultsim_integrity_check
126 if {$testrc==0} { compare_db db db2 }
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 {
138 faultsim_restore_and_reopen
141 sqlite3session S db main
144 for {set i 0} {$i < 125} {incr i} {
145 execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)}
148 for {set i 125} {$i < 133} {incr i} {
149 execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)}
154 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
156 sqlite3changeset_apply db2 [S changeset] xConflict
160 faultsim_integrity_check
165 forcedelete test.db2 test.db
169 proc xConflict {op tbl type args} {
170 if { $type=="CONFLICT" || $type=="DATA" } {
178 PRAGMA encoding = 'utf16';
179 CREATE TABLE t1(a PRIMARY KEY, b);
180 INSERT INTO t1 VALUES(5, 32);
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);
191 faultsim_save_and_close
194 do_faultsim_test 4 -faults oom-* -prep {
197 faultsim_restore_and_reopen
199 sqlite3session S db main
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;
208 sqlite3changeset_apply db2 [S changeset] xConflict
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.
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;
237 do_faultsim_test 5.1 -faults oom* -body {
238 set ::inverse [sqlite3changeset_invert $::changeset]
241 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
244 sqlite3session_foreach c $::inverse { lappend x $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 {} {}}}
250 if {$x != $y} { error "changeset no good" }
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);
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]
277 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
280 sqlite3session_foreach c $::inverse { lappend x $c }
282 {UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}}
283 {UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}}
285 if {$x != $y} { error "changeset no good" }
293 set abc [string repeat abc 256]
294 set def [string repeat def 256]
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;
305 do_faultsim_test 5.3 -faults oom-tra* -body {
306 set ::inverse [sqlite3changeset_invert $::changeset]
309 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
312 sqlite3session_foreach c $::inverse { lappend x $c }
314 {INSERT t2 0 X. {} {i 1 t $::abc}}
315 {DELETE t2 0 X. {i 2 t $::def} {}}
317 if {$x != $y} { error "changeset no good" }
321 #-------------------------------------------------------------------------
322 # Test that OOM errors in sqlite3changeset_concat() are handled correctly.
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);
336 sqlite3session M db main
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;
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');
349 set ::total [changeset_to_list [M changeset]]
353 do_faultsim_test 6 -faults oom-* -body {
354 set ::result [sqlite3changeset_concat $::c1 $::c2]
357 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
359 set v [changeset_to_list $::result]
360 if {$v != $::total} { error "result no good" }
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
371 for {set ::i 0} {$::i < 480} {incr ::i 4} {
372 lappend res "INSERT t1 0 X. {} {i $::i i $::i}"
375 do_faultsim_test 7 -faults oom-transient -prep {
377 faultsim_restore_and_reopen
378 sqlite3session S db main
381 for {set ::i 0} {$::i < 480} {incr ::i 4} {
382 execsql {INSERT INTO t1 VALUES($::i, $::i)}
385 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
387 set cres [list [catch {changeset_to_list [S changeset]} msg] $msg]
389 if {$cres != "1 SQLITE_NOMEM" && $cres != "0 {$::res}"} {
390 error "Expected {0 $::res} Got {$cres}"
393 catch { S changeset }
398 faultsim_delete_and_reopen
400 sqlite3session S db main
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);
408 set ::changeset [S changeset]
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 {
419 sqlite3session_foreach -next v $::changeset { lappend ::res $v }
420 normalize_list $::res
422 faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
424 do_faultsim_test 8.2 -faults oom* -body {
426 sqlite3session_foreach v $::changeset { lappend ::res $v }
427 normalize_list $::res
429 faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
432 faultsim_delete_and_reopen
435 PRAGMA encoding = 'utf16';
436 CREATE TABLE t1(a PRIMARY KEY, b);
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 {
446 faultsim_restore_and_reopen
447 sqlite3session S db main
451 INSERT INTO t1 VALUES('abcdefghijklmnopqrstuv', 'ABCDEFGHIJKLMNOPQRSTUV');
453 set ::c [S changeset]
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}}
462 if { [changeset_to_list $::c] != $expected } {
463 error "changeset mismatch"
468 faultsim_delete_and_reopen
471 PRAGMA encoding = 'utf16';
472 CREATE TABLE t1(a PRIMARY KEY, b);
473 INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV');
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 {
483 faultsim_restore_and_reopen
484 sqlite3session S db main
488 UPDATE t1 SET b = 'xyz';
490 set ::c [S changeset]
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}}
499 if { [changeset_to_list $::c] != $expected } {
500 error "changeset mismatch"
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
513 CREATE TABLE t1(a PRIMARY KEY, b);
514 INSERT INTO t1 VALUES($bigstr, $bigstr);
517 sqlite3session S db main
519 execsql { UPDATE t1 SET b = b||'x' }
522 execsql { UPDATE t1 SET b = b||'xyz' }
524 faultsim_save_and_close
526 faultsim_restore_and_reopen
528 proc xConflict {args} { return "ABORT" }
529 list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
531 do_execsql_test 10.prep.3 { SELECT b=$bigstr||'x' FROM t1 } 0
533 proc xConflict {args} { return "REPLACE" }
534 list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
536 do_execsql_test 10.prep.5 { SELECT b=$bigstr||'x' FROM t1 } 1
539 do_faultsim_test 10 -faults oom-tra* -prep {
540 faultsim_restore_and_reopen
542 sqlite3changeset_apply_replace_all db $::C
544 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
546 if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} {
547 error "data does not look right"
552 #-------------------------------------------------------------------------
553 # Test an OOM with an sqlite3changeset_apply() filter callback.
558 CREATE TABLE t1(a PRIMARY KEY, b);
559 CREATE TABLE t2(x PRIMARY KEY, y);
563 set ::cs [changeset_from_sql {
564 INSERT INTO t1 VALUES(1, 2);
565 INSERT INTO t2 VALUES('x', 'y');
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
578 sqlite3changeset_apply db $::cs {} filter
580 faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
582 if {[db eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2}] != "1 2"} {
583 error "data does not look right"