112ca0b56Sdan# 2011 Mar 21
212ca0b56Sdan#
312ca0b56Sdan# The author disclaims copyright to this source code.  In place of
412ca0b56Sdan# a legal notice, here is a blessing:
512ca0b56Sdan#
612ca0b56Sdan#    May you do good and not evil.
712ca0b56Sdan#    May you find forgiveness for yourself and forgive others.
812ca0b56Sdan#    May you share freely, never taking more than you give.
912ca0b56Sdan#
1012ca0b56Sdan#***********************************************************************
1112ca0b56Sdan#
1212ca0b56Sdan# The focus of this file is testing the session module.
1312ca0b56Sdan#
1412ca0b56Sdan
1512ca0b56Sdanif {![info exists testdir]} {
1612ca0b56Sdan  set testdir [file join [file dirname [info script]] .. .. test]
1712ca0b56Sdan}
1812ca0b56Sdansource [file join [file dirname [info script]] session_common.tcl]
1912ca0b56Sdansource $testdir/tester.tcl
20*05accd22Sdanifcapable !session {finish_test; return}
2112ca0b56Sdan
2212ca0b56Sdanset testprefix sessionfault
2312ca0b56Sdan
2412ca0b56Sdanforcedelete test.db2
2512ca0b56Sdansqlite3 db2 test.db2
2612ca0b56Sdando_common_sql {
2712ca0b56Sdan  CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
2812ca0b56Sdan  INSERT INTO t1 VALUES(1, 2, 3);
2912ca0b56Sdan  INSERT INTO t1 VALUES(4, 5, 6);
3012ca0b56Sdan}
3112ca0b56Sdanfaultsim_save_and_close
3212ca0b56Sdandb2 close
3312ca0b56Sdan
347aa469cdSdan#-------------------------------------------------------------------------
3512ca0b56Sdan# Test OOM error handling when collecting and applying a simple changeset.
3612ca0b56Sdan#
37245b49b2Sdan# Test 1.1 attaches tables individually by name to the session object.
38245b49b2Sdan# Whereas test 1.2 passes NULL to sqlite3session_attach() to attach all
39245b49b2Sdan# tables.
40245b49b2Sdan#
41b4480e94Sdando_faultsim_test 1.1 -faults oom-* -prep {
4212ca0b56Sdan  catch {db2 close}
4312ca0b56Sdan  catch {db close}
4412ca0b56Sdan  faultsim_restore_and_reopen
4512ca0b56Sdan  sqlite3 db2 test.db2
4612ca0b56Sdan} -body {
4712ca0b56Sdan  do_then_apply_sql {
48e5754eecSdan    INSERT INTO t1 VALUES('a string value', 8, 9);
4912ca0b56Sdan    UPDATE t1 SET c = 10 WHERE a = 1;
5012ca0b56Sdan    DELETE FROM t1 WHERE a = 4;
5112ca0b56Sdan  }
5212ca0b56Sdan} -test {
5312ca0b56Sdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
5412ca0b56Sdan  faultsim_integrity_check
5512ca0b56Sdan  if {$testrc==0} { compare_db db db2 }
5612ca0b56Sdan}
5712ca0b56Sdan
58b4480e94Sdando_faultsim_test 1.2 -faults oom-* -prep {
59245b49b2Sdan  catch {db2 close}
60245b49b2Sdan  catch {db close}
61245b49b2Sdan  faultsim_restore_and_reopen
62245b49b2Sdan} -body {
63245b49b2Sdan  sqlite3session S db main
64245b49b2Sdan  S attach *
65245b49b2Sdan  execsql {
66e5754eecSdan    INSERT INTO t1 VALUES('a string value', 8, 9);
67245b49b2Sdan    UPDATE t1 SET c = 10 WHERE a = 1;
68245b49b2Sdan    DELETE FROM t1 WHERE a = 4;
69245b49b2Sdan  }
70245b49b2Sdan  set ::changeset [S changeset]
71245b49b2Sdan  set {} {}
72245b49b2Sdan} -test {
73245b49b2Sdan  catch { S delete }
74245b49b2Sdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
75245b49b2Sdan  faultsim_integrity_check
76245b49b2Sdan  if {$testrc==0} {
77245b49b2Sdan    proc xConflict {args} { return "OMIT" }
78245b49b2Sdan    sqlite3 db2 test.db2
79245b49b2Sdan    sqlite3changeset_apply db2 $::changeset xConflict
80245b49b2Sdan    compare_db db db2
81245b49b2Sdan  }
82245b49b2Sdan}
83245b49b2Sdan
847aa469cdSdan#-------------------------------------------------------------------------
85b4480e94Sdan# The following block of tests - 2.* - are designed to check
867aa469cdSdan# the handling of faults in the sqlite3changeset_apply() function.
877aa469cdSdan#
887aa469cdSdancatch {db close}
897aa469cdSdancatch {db2 close}
907aa469cdSdanforcedelete test.db2 test.db
917aa469cdSdansqlite3 db2 test.db2
927aa469cdSdansqlite3 db test.db
937aa469cdSdando_common_sql {
947aa469cdSdan  CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
957aa469cdSdan  INSERT INTO t1 VALUES('apple', 'orange', 'pear');
967aa469cdSdan
977aa469cdSdan  CREATE TABLE t2(x PRIMARY KEY, y);
987aa469cdSdan}
997aa469cdSdandb2 close
1007aa469cdSdanfaultsim_save_and_close
1017aa469cdSdan
1027aa469cdSdan
1037aa469cdSdanforeach {tn conflict_policy sql sql2} {
1047aa469cdSdan  1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {}
1057aa469cdSdan  2 OMIT { DELETE FROM t1 WHERE a = 'apple' }                         {}
1067aa469cdSdan  3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' }            {}
1077aa469cdSdan  4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } {
1087aa469cdSdan    INSERT INTO t2 VALUES('keyvalue', 'value 2');
1097aa469cdSdan  }
1107aa469cdSdan} {
1117aa469cdSdan  proc xConflict args [list return $conflict_policy]
1127aa469cdSdan
113b4480e94Sdan  do_faultsim_test 2.$tn -faults oom-transient -prep {
1147aa469cdSdan    catch {db2 close}
1157aa469cdSdan    catch {db close}
1167aa469cdSdan    faultsim_restore_and_reopen
1177aa469cdSdan    set ::changeset [changeset_from_sql $::sql]
1187aa469cdSdan    sqlite3 db2 test.db2
1197aa469cdSdan    sqlite3_db_config_lookaside db2 0 0 0
1207aa469cdSdan    execsql $::sql2 db2
1217aa469cdSdan  } -body {
1227aa469cdSdan    sqlite3changeset_apply db2 $::changeset xConflict
1237aa469cdSdan  } -test {
1247aa469cdSdan    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
1257aa469cdSdan    faultsim_integrity_check
1267aa469cdSdan    if {$testrc==0} { compare_db db db2 }
1277aa469cdSdan  }
1287aa469cdSdan}
1297aa469cdSdan
1307aa469cdSdan#-------------------------------------------------------------------------
13112ca0b56Sdan# This test case is designed so that a malloc() failure occurs while
13212ca0b56Sdan# resizing the session object hash-table from 256 to 512 buckets. This
13312ca0b56Sdan# is not an error, just a sub-optimal condition.
13412ca0b56Sdan#
135b4480e94Sdando_faultsim_test 3 -faults oom-* -prep {
13612ca0b56Sdan  catch {db2 close}
13712ca0b56Sdan  catch {db close}
13812ca0b56Sdan  faultsim_restore_and_reopen
13912ca0b56Sdan  sqlite3 db2 test.db2
14012ca0b56Sdan
14112ca0b56Sdan  sqlite3session S db main
14212ca0b56Sdan  S attach t1
14312ca0b56Sdan  execsql { BEGIN }
14412ca0b56Sdan  for {set i 0} {$i < 125} {incr i} {
14512ca0b56Sdan    execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)}
14612ca0b56Sdan  }
14712ca0b56Sdan} -body {
14812ca0b56Sdan  for {set i 125} {$i < 133} {incr i} {
14912ca0b56Sdan    execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)}
15012ca0b56Sdan  }
15112ca0b56Sdan  S changeset
15212ca0b56Sdan  set {} {}
15312ca0b56Sdan} -test {
15412ca0b56Sdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
15512ca0b56Sdan  if {$testrc==0} {
15612ca0b56Sdan    sqlite3changeset_apply db2 [S changeset] xConflict
15712ca0b56Sdan    compare_db db db2
15812ca0b56Sdan  }
15912ca0b56Sdan  catch { S delete }
16012ca0b56Sdan  faultsim_integrity_check
16112ca0b56Sdan}
16212ca0b56Sdan
163db04571cSdancatch { db close }
164db04571cSdancatch { db2 close }
165db04571cSdanforcedelete test.db2 test.db
166db04571cSdansqlite3 db2 test.db2
167db04571cSdansqlite3 db test.db
168db04571cSdan
169db04571cSdanproc xConflict {op tbl type args} {
170db04571cSdan  if { $type=="CONFLICT" || $type=="DATA" } {
171db04571cSdan    return "REPLACE"
172db04571cSdan  }
173db04571cSdan  return "OMIT"
174db04571cSdan}
175db04571cSdan
1767aa469cdSdando_test 4.0 {
177db04571cSdan  execsql {
178db04571cSdan    PRAGMA encoding = 'utf16';
179db04571cSdan    CREATE TABLE t1(a PRIMARY KEY, b);
180db04571cSdan    INSERT INTO t1 VALUES(5, 32);
181db04571cSdan  }
182db04571cSdan  execsql {
183db04571cSdan    PRAGMA encoding = 'utf16';
184db04571cSdan    CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
185db04571cSdan    INSERT INTO t1 VALUES(1, 2);
186db04571cSdan    INSERT INTO t1 VALUES(2, 4);
187db04571cSdan    INSERT INTO t1 VALUES(4, 16);
188db04571cSdan  } db2
189db04571cSdan} {}
190db04571cSdan
191db04571cSdanfaultsim_save_and_close
192db04571cSdandb2 close
193db04571cSdan
194b4480e94Sdando_faultsim_test 4 -faults oom-* -prep {
195db04571cSdan  catch {db2 close}
196db04571cSdan  catch {db close}
197db04571cSdan  faultsim_restore_and_reopen
198db04571cSdan  sqlite3 db2 test.db2
199db04571cSdan  sqlite3session S db main
200db04571cSdan  S attach t1
201db04571cSdan  execsql {
202db04571cSdan    INSERT INTO t1 VALUES(1, 45);
203db04571cSdan    INSERT INTO t1 VALUES(2, 55);
204db04571cSdan    INSERT INTO t1 VALUES(3, 55);
205db04571cSdan    UPDATE t1 SET a = 4 WHERE a = 5;
206db04571cSdan  }
207db04571cSdan} -body {
208db04571cSdan  sqlite3changeset_apply db2 [S changeset] xConflict
209db04571cSdan} -test {
210db04571cSdan  catch { S delete }
211db04571cSdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
212db04571cSdan  if {$testrc==0} { compare_db db db2 }
213db04571cSdan}
214db04571cSdan
2157aa469cdSdan#-------------------------------------------------------------------------
2167aa469cdSdan# This block of tests verifies that OOM faults in the
2177aa469cdSdan# sqlite3changeset_invert() function are handled correctly.
2187aa469cdSdan#
2197aa469cdSdancatch {db close}
2207aa469cdSdancatch {db2 close}
2217aa469cdSdanforcedelete test.db
2227aa469cdSdansqlite3 db test.db
2237aa469cdSdanexecsql {
2247aa469cdSdan  CREATE TABLE t1(a, b, PRIMARY KEY(b));
2257aa469cdSdan  CREATE TABLE t2(a PRIMARY KEY, b);
2267aa469cdSdan  INSERT INTO t1 VALUES('string', 1);
2277aa469cdSdan  INSERT INTO t1 VALUES(4, 2);
2287aa469cdSdan  INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
2297aa469cdSdan}
2307aa469cdSdanset changeset [changeset_from_sql {
2317aa469cdSdan  INSERT INTO t1 VALUES('xxx', 'yyy');
2327aa469cdSdan  DELETE FROM t1 WHERE a = 'string';
2337aa469cdSdan  UPDATE t1 SET a = 20 WHERE b = 2;
2347aa469cdSdan}]
2357aa469cdSdandb close
2367aa469cdSdan
237e8fa8c96Sdando_faultsim_test 5.1 -faults oom* -body {
2387aa469cdSdan  set ::inverse [sqlite3changeset_invert $::changeset]
2397aa469cdSdan  set {} {}
2407aa469cdSdan} -test {
2417aa469cdSdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
2427aa469cdSdan  if {$testrc==0} {
2437aa469cdSdan    set x [list]
2447aa469cdSdan    sqlite3session_foreach c $::inverse { lappend x $c }
2457aa469cdSdan    foreach c {
246244593c8Sdan        {DELETE t1 0 .X {t xxx t yyy} {}}
247244593c8Sdan        {INSERT t1 0 .X {} {t string i 1}}
2481e556c3fSdan        {UPDATE t1 0 .X {i 20 i 2} {i 4 {} {}}}
2497aa469cdSdan    } { lappend y $c }
2507aa469cdSdan    if {$x != $y} { error "changeset no good" }
2517aa469cdSdan  }
2527aa469cdSdan}
2537aa469cdSdan
254e8fa8c96Sdancatch {db close}
255e8fa8c96Sdancatch {db2 close}
256e8fa8c96Sdanforcedelete test.db
257e8fa8c96Sdansqlite3 db test.db
258e8fa8c96Sdanexecsql {
259e8fa8c96Sdan  CREATE TABLE t2(a PRIMARY KEY, b);
260e8fa8c96Sdan  INSERT INTO t2 VALUES(1, 'abc');
261e8fa8c96Sdan  INSERT INTO t2 VALUES(2, 'def');
262e8fa8c96Sdan}
263e8fa8c96Sdanset changeset [changeset_from_sql {
264e8fa8c96Sdan  UPDATE t2 SET b = (b || b || b || b);
265e8fa8c96Sdan  UPDATE t2 SET b = (b || b || b || b);
266e8fa8c96Sdan  UPDATE t2 SET b = (b || b || b || b);
267e8fa8c96Sdan  UPDATE t2 SET b = (b || b || b || b);
268e8fa8c96Sdan}]
269e8fa8c96Sdandb close
270e8fa8c96Sdanset abc [string repeat abc 256]
271e8fa8c96Sdanset def [string repeat def 256]
272e8fa8c96Sdan
273e8fa8c96Sdando_faultsim_test 5.2 -faults oom-tra* -body {
274e8fa8c96Sdan  set ::inverse [sqlite3changeset_invert $::changeset]
275e8fa8c96Sdan  set {} {}
276e8fa8c96Sdan} -test {
277e8fa8c96Sdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
278e8fa8c96Sdan  if {$testrc==0} {
279e8fa8c96Sdan    set x [list]
280e8fa8c96Sdan    sqlite3session_foreach c $::inverse { lappend x $c }
281e8fa8c96Sdan    foreach c "
282e8fa8c96Sdan        {UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}}
283e8fa8c96Sdan        {UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}}
284e8fa8c96Sdan    " { lappend y $c }
285e8fa8c96Sdan    if {$x != $y} { error "changeset no good" }
286e8fa8c96Sdan  }
287e8fa8c96Sdan}
288e8fa8c96Sdan
289f05ac112Sdancatch {db close}
290f05ac112Sdancatch {db2 close}
291f05ac112Sdanforcedelete test.db
292f05ac112Sdansqlite3 db test.db
293f05ac112Sdanset abc [string repeat abc 256]
294f05ac112Sdanset def [string repeat def 256]
295f05ac112Sdanexecsql "
296f05ac112Sdan  CREATE TABLE t2(a PRIMARY KEY, b);
297f05ac112Sdan  INSERT INTO t2 VALUES(1, '$abc');
298f05ac112Sdan"
299f05ac112Sdanset changeset [changeset_from_sql "
300f05ac112Sdan  INSERT INTO t2 VALUES(2, '$def');
301f05ac112Sdan  DELETE FROM t2 WHERE a = 1;
302f05ac112Sdan"]
303f05ac112Sdandb close
304f05ac112Sdan
305f05ac112Sdando_faultsim_test 5.3 -faults oom-tra* -body {
306f05ac112Sdan  set ::inverse [sqlite3changeset_invert $::changeset]
307f05ac112Sdan  set {} {}
308f05ac112Sdan} -test {
309f05ac112Sdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
310f05ac112Sdan  if {$testrc==0} {
311f05ac112Sdan    set x [list]
312f05ac112Sdan    sqlite3session_foreach c $::inverse { lappend x $c }
313f05ac112Sdan    foreach c "
314f05ac112Sdan        {INSERT t2 0 X. {} {i 1 t $::abc}}
315f05ac112Sdan        {DELETE t2 0 X. {i 2 t $::def} {}}
316f05ac112Sdan    " { lappend y $c }
317f05ac112Sdan    if {$x != $y} { error "changeset no good" }
318f05ac112Sdan  }
319f05ac112Sdan}
320f05ac112Sdan
3211756ae10Sdan#-------------------------------------------------------------------------
3221756ae10Sdan# Test that OOM errors in sqlite3changeset_concat() are handled correctly.
3231756ae10Sdan#
3241756ae10Sdancatch {db close}
3251756ae10Sdanforcedelete test.db
3261756ae10Sdansqlite3 db test.db
3271756ae10Sdando_execsql_test 5.prep1 {
3281756ae10Sdan  CREATE TABLE t1(a, b, PRIMARY KEY(b));
3291756ae10Sdan  CREATE TABLE t2(a PRIMARY KEY, b);
3301756ae10Sdan  INSERT INTO t1 VALUES('string', 1);
3311756ae10Sdan  INSERT INTO t1 VALUES(4, 2);
3321756ae10Sdan  INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
3331756ae10Sdan}
3341756ae10Sdan
3356734007dSdando_test 6.prep2 {
3361756ae10Sdan  sqlite3session M db main
3371756ae10Sdan  M attach *
3381756ae10Sdan  set ::c2 [changeset_from_sql {
3391756ae10Sdan    INSERT INTO t2 VALUES(randomblob(1000), randomblob(1000));
3401756ae10Sdan    INSERT INTO t2 VALUES('one', 'two');
3411756ae10Sdan    INSERT INTO t2 VALUES(1, NULL);
3421756ae10Sdan    UPDATE t1 SET a = 5 WHERE a = 2;
3431756ae10Sdan  }]
3441756ae10Sdan  set ::c1 [changeset_from_sql {
3451756ae10Sdan    DELETE FROM t2 WHERE a = 1;
3461756ae10Sdan    UPDATE t1 SET a = 4 WHERE a = 2;
3471756ae10Sdan    INSERT INTO t2 VALUES('x', 'y');
3481756ae10Sdan  }]
3491756ae10Sdan  set ::total [changeset_to_list [M changeset]]
3501756ae10Sdan  M delete
3511756ae10Sdan} {}
3521756ae10Sdan
3531756ae10Sdando_faultsim_test 6 -faults oom-* -body {
3541756ae10Sdan  set ::result [sqlite3changeset_concat $::c1 $::c2]
3551756ae10Sdan  set {} {}
3561756ae10Sdan} -test {
3571756ae10Sdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
3581756ae10Sdan  if {$testrc==0} {
3591756ae10Sdan    set v [changeset_to_list $::result]
3601756ae10Sdan    if {$v != $::total} { error "result no good" }
3611756ae10Sdan  }
3621756ae10Sdan}
3631756ae10Sdan
3646734007dSdanfaultsim_delete_and_reopen
3656fee7958Sdando_execsql_test 7.prep1 {
3666734007dSdan  CREATE TABLE t1(a, b, PRIMARY KEY(a));
3676734007dSdan}
3686734007dSdanfaultsim_save_and_close
3696734007dSdan
3706734007dSdanset res [list]
3716734007dSdanfor {set ::i 0} {$::i < 480} {incr ::i 4} {
3726734007dSdan  lappend res "INSERT t1 0 X. {} {i $::i i $::i}"
3736734007dSdan}
3746734007dSdanset res [lsort $res]
3756734007dSdando_faultsim_test 7 -faults oom-transient -prep {
3766fee7958Sdan  catch { S delete }
3776734007dSdan  faultsim_restore_and_reopen
3786734007dSdan  sqlite3session S db main
3796734007dSdan  S attach *
3806734007dSdan} -body {
3816734007dSdan  for {set ::i 0} {$::i < 480} {incr ::i 4} {
3826734007dSdan    execsql {INSERT INTO t1 VALUES($::i, $::i)}
3836734007dSdan  }
3846734007dSdan} -test {
3856734007dSdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
3866734007dSdan  if {$testrc==0} {
3876734007dSdan    set cres [list [catch {changeset_to_list [S changeset]} msg] $msg]
3886734007dSdan    S delete
3896734007dSdan    if {$cres != "1 SQLITE_NOMEM" && $cres != "0 {$::res}"} {
3906734007dSdan      error "Expected {0 $::res} Got {$cres}"
3916734007dSdan    }
3926734007dSdan  } else {
393a66e3862Sdan    catch { S changeset }
394a66e3862Sdan    catch { S delete }
3956734007dSdan  }
3966734007dSdan}
3976734007dSdan
3986734007dSdanfaultsim_delete_and_reopen
3996734007dSdando_test 8.prep {
4006734007dSdan  sqlite3session S db main
4016734007dSdan  S attach *
4026734007dSdan  execsql {
4036734007dSdan    CREATE TABLE t1(a, b, PRIMARY KEY(a));
4046734007dSdan    INSERT INTO t1 VALUES(1, 2);
4056734007dSdan    INSERT INTO t1 VALUES(3, 4);
4066734007dSdan    INSERT INTO t1 VALUES(5, 6);
4076734007dSdan  }
4086734007dSdan  set ::changeset [S changeset]
4096734007dSdan  S delete
4106734007dSdan} {}
4116734007dSdan
4126734007dSdanset expected [normalize_list {
4136734007dSdan  {INSERT t1 0 X. {} {i 1 i 2}}
4146734007dSdan  {INSERT t1 0 X. {} {i 3 i 4}}
4156734007dSdan  {INSERT t1 0 X. {} {i 5 i 6}}
4166734007dSdan}]
4176734007dSdando_faultsim_test 8.1 -faults oom* -body {
4186734007dSdan  set ::res [list]
4196734007dSdan  sqlite3session_foreach -next v $::changeset { lappend ::res $v }
4206734007dSdan  normalize_list $::res
4216734007dSdan} -test {
4226734007dSdan  faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
4236734007dSdan}
4246734007dSdando_faultsim_test 8.2 -faults oom* -body {
4256734007dSdan  set ::res [list]
4266734007dSdan  sqlite3session_foreach v $::changeset { lappend ::res $v }
4276734007dSdan  normalize_list $::res
4286734007dSdan} -test {
4296734007dSdan  faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
4306734007dSdan}
4316734007dSdan
4326734007dSdanfaultsim_delete_and_reopen
43380fe2d93Sdando_test 9.1.prep {
4346734007dSdan  execsql {
4356734007dSdan    PRAGMA encoding = 'utf16';
4366734007dSdan    CREATE TABLE t1(a PRIMARY KEY, b);
4376734007dSdan  }
4386734007dSdan} {}
4396734007dSdanfaultsim_save_and_close
4406734007dSdan
44140eaa086Sdrhset answers [list {0 {}} {1 SQLITE_NOMEM} \
44240eaa086Sdrh                  {1 {callback requested query abort}} \
44340eaa086Sdrh                  {1 {abort due to ROLLBACK}}]
44480fe2d93Sdando_faultsim_test 9.1 -faults oom-transient -prep {
4456734007dSdan  catch { unset ::c }
4466734007dSdan  faultsim_restore_and_reopen
4476734007dSdan  sqlite3session S db main
4486734007dSdan  S attach *
4496734007dSdan} -body {
4506734007dSdan  execsql {
4516734007dSdan    INSERT INTO t1 VALUES('abcdefghijklmnopqrstuv', 'ABCDEFGHIJKLMNOPQRSTUV');
4526734007dSdan  }
4536734007dSdan  set ::c [S changeset]
4546734007dSdan  set {} {}
4556734007dSdan} -test {
4566734007dSdan  S delete
45780fe2d93Sdan  eval faultsim_test_result $::answers
4586734007dSdan  if {[info exists ::c]} {
45980fe2d93Sdan    set expected [normalize_list {
46080fe2d93Sdan      {INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}}
46180fe2d93Sdan    }]
4626734007dSdan    if { [changeset_to_list $::c] != $expected } {
4636734007dSdan      error "changeset mismatch"
4646734007dSdan    }
4656734007dSdan  }
4666734007dSdan}
4676734007dSdan
46880fe2d93Sdanfaultsim_delete_and_reopen
46980fe2d93Sdando_test 9.2.prep {
47080fe2d93Sdan  execsql {
47180fe2d93Sdan    PRAGMA encoding = 'utf16';
47280fe2d93Sdan    CREATE TABLE t1(a PRIMARY KEY, b);
47380fe2d93Sdan    INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV');
47480fe2d93Sdan  }
47580fe2d93Sdan} {}
47680fe2d93Sdanfaultsim_save_and_close
47780fe2d93Sdan
47840eaa086Sdrhset answers [list {0 {}} {1 SQLITE_NOMEM} \
47940eaa086Sdrh                  {1 {callback requested query abort}} \
48040eaa086Sdrh                  {1 {abort due to ROLLBACK}}]
48180fe2d93Sdando_faultsim_test 9.2 -faults oom-transient -prep {
48280fe2d93Sdan  catch { unset ::c }
48380fe2d93Sdan  faultsim_restore_and_reopen
48480fe2d93Sdan  sqlite3session S db main
48580fe2d93Sdan  S attach *
48680fe2d93Sdan} -body {
48780fe2d93Sdan  execsql {
48880fe2d93Sdan    UPDATE t1 SET b = 'xyz';
48980fe2d93Sdan  }
49080fe2d93Sdan  set ::c [S changeset]
49180fe2d93Sdan  set {} {}
49280fe2d93Sdan} -test {
49380fe2d93Sdan  S delete
49480fe2d93Sdan  eval faultsim_test_result $::answers
49580fe2d93Sdan  if {[info exists ::c]} {
49680fe2d93Sdan    set expected [normalize_list {
49780fe2d93Sdan      {UPDATE t1 0 X. {t abcdefghij t ABCDEFGHIJKLMNOPQRSTUV} {{} {} t xyz}}
49880fe2d93Sdan    }]
49980fe2d93Sdan    if { [changeset_to_list $::c] != $expected } {
50080fe2d93Sdan      error "changeset mismatch"
50180fe2d93Sdan    }
50280fe2d93Sdan  }
50380fe2d93Sdan}
50480fe2d93Sdan
505082c96dfSdan#-------------------------------------------------------------------------
506082c96dfSdan# Test that if a conflict-handler encounters an OOM in
507082c96dfSdan# sqlite3_value_text() but goes on to return SQLITE_CHANGESET_REPLACE
508082c96dfSdan# anyway, the OOM is picked up by the sessions module.
509082c96dfSdanset bigstr [string repeat abcdefghij 100]
510082c96dfSdanfaultsim_delete_and_reopen
511082c96dfSdando_test 10.prep.1  {
512082c96dfSdan  execsql {
513082c96dfSdan    CREATE TABLE t1(a PRIMARY KEY, b);
514082c96dfSdan    INSERT INTO t1 VALUES($bigstr, $bigstr);
515082c96dfSdan  }
516082c96dfSdan
517082c96dfSdan  sqlite3session S db main
518082c96dfSdan  S attach *
519082c96dfSdan  execsql { UPDATE t1 SET b = b||'x' }
520082c96dfSdan  set C [S changeset]
521082c96dfSdan  S delete
522082c96dfSdan  execsql { UPDATE t1 SET b = b||'xyz' }
523082c96dfSdan} {}
524082c96dfSdanfaultsim_save_and_close
525082c96dfSdan
526082c96dfSdanfaultsim_restore_and_reopen
527082c96dfSdando_test 10.prep.2  {
528082c96dfSdan  proc xConflict {args} { return "ABORT" }
529082c96dfSdan  list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
530082c96dfSdan} {1 SQLITE_ABORT}
531082c96dfSdando_execsql_test 10.prep.3 { SELECT b=$bigstr||'x' FROM t1 } 0
532082c96dfSdando_test 10.prep.4  {
533082c96dfSdan  proc xConflict {args} { return "REPLACE" }
534082c96dfSdan  list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
535082c96dfSdan} {0 {}}
536082c96dfSdando_execsql_test 10.prep.5 { SELECT b=$bigstr||'x' FROM t1 } 1
537082c96dfSdandb close
538082c96dfSdan
539082c96dfSdando_faultsim_test 10 -faults oom-tra* -prep {
540082c96dfSdan  faultsim_restore_and_reopen
541082c96dfSdan} -body {
542082c96dfSdan  sqlite3changeset_apply_replace_all db $::C
543082c96dfSdan} -test {
544082c96dfSdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
545082c96dfSdan  if {$testrc==0} {
546082c96dfSdan    if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} {
547082c96dfSdan      error "data does not look right"
548082c96dfSdan    }
549082c96dfSdan  }
550082c96dfSdan}
551082c96dfSdan
552f05ac112Sdan#-------------------------------------------------------------------------
553f05ac112Sdan# Test an OOM with an sqlite3changeset_apply() filter callback.
554f05ac112Sdan#
555f05ac112Sdanreset_db
556f05ac112Sdando_test 11.prep {
557f05ac112Sdan  execsql {
558f05ac112Sdan    CREATE TABLE t1(a PRIMARY KEY, b);
559f05ac112Sdan    CREATE TABLE t2(x PRIMARY KEY, y);
560f05ac112Sdan    BEGIN;
561f05ac112Sdan  }
562082c96dfSdan
563f05ac112Sdan  set ::cs [changeset_from_sql {
564f05ac112Sdan    INSERT INTO t1 VALUES(1, 2);
565f05ac112Sdan    INSERT INTO t2 VALUES('x', 'y');
566f05ac112Sdan  }]
567f05ac112Sdan
568f05ac112Sdan  execsql ROLLBACK
569f05ac112Sdan  set {} {}
570f05ac112Sdan} {}
571f05ac112Sdan
572f05ac112Sdanproc filter {x} { return [string equal t1 $x] }
573f05ac112Sdanfaultsim_save_and_close
574f05ac112Sdan
575f05ac112Sdando_faultsim_test 11 -faults oom-tra* -prep {
576f05ac112Sdan  faultsim_restore_and_reopen
577f05ac112Sdan} -body {
578f05ac112Sdan  sqlite3changeset_apply db $::cs {} filter
579f05ac112Sdan} -test {
580f05ac112Sdan  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
581f05ac112Sdan  if {$testrc==0} {
582f05ac112Sdan    if {[db eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2}] != "1 2"} {
583f05ac112Sdan      error "data does not look right"
584f05ac112Sdan    }
585f05ac112Sdan  }
586f05ac112Sdan}
58780fe2d93Sdan
5886734007dSdan
58912ca0b56Sdanfinish_test
590