mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Add further tests and documentation for the sessions rebase feature.
FossilOrigin-Name: 7475a363ebb272ae23c0796fe7587714a156dc6a3a4a57ed948ed6f69d3c1218
This commit is contained in:
@ -169,3 +169,4 @@ proc changeset_to_list {c} {
|
|||||||
sqlite3session_foreach elem $c { lappend list $elem }
|
sqlite3session_foreach elem $c { lappend list $elem }
|
||||||
lsort $list
|
lsort $list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ source $testdir/tester.tcl
|
|||||||
ifcapable !session {finish_test; return}
|
ifcapable !session {finish_test; return}
|
||||||
set testprefix sessionfault2
|
set testprefix sessionfault2
|
||||||
|
|
||||||
|
if 1 {
|
||||||
|
|
||||||
do_execsql_test 1.0.0 {
|
do_execsql_test 1.0.0 {
|
||||||
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
|
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
|
||||||
INSERT INTO t1 VALUES(1, 1);
|
INSERT INTO t1 VALUES(1, 1);
|
||||||
@ -103,5 +105,136 @@ do_faultsim_test 2 -faults oom-p* -prep {
|
|||||||
faultsim_integrity_check
|
faultsim_integrity_check
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# OOM when collecting and apply a changeset that uses sqlite_stat1.
|
||||||
|
#
|
||||||
|
reset_db
|
||||||
|
forcedelete test.db2
|
||||||
|
sqlite3 db2 test.db2
|
||||||
|
do_common_sql {
|
||||||
|
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE, c);
|
||||||
|
CREATE INDEX i1 ON t1(c);
|
||||||
|
INSERT INTO t1 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t1 VALUES(4, 5, 6);
|
||||||
|
INSERT INTO t1 VALUES(7, 8, 9);
|
||||||
|
CREATE TABLE t2(a, b, c);
|
||||||
|
INSERT INTO t2 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t2 VALUES(4, 5, 6);
|
||||||
|
INSERT INTO t2 VALUES(7, 8, 9);
|
||||||
|
ANALYZE;
|
||||||
|
}
|
||||||
|
faultsim_save_and_close
|
||||||
|
db2 close
|
||||||
|
|
||||||
|
do_faultsim_test 1.1 -faults oom-* -prep {
|
||||||
|
catch {db2 close}
|
||||||
|
catch {db close}
|
||||||
|
faultsim_restore_and_reopen
|
||||||
|
sqlite3 db2 test.db2
|
||||||
|
} -body {
|
||||||
|
do_then_apply_sql {
|
||||||
|
INSERT INTO sqlite_stat1 VALUES('x', 'y', 45);
|
||||||
|
UPDATE sqlite_stat1 SET stat = 123 WHERE tbl='t1' AND idx='i1';
|
||||||
|
UPDATE sqlite_stat1 SET stat = 456 WHERE tbl='t2';
|
||||||
|
}
|
||||||
|
} -test {
|
||||||
|
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||||
|
faultsim_integrity_check
|
||||||
|
if {$testrc==0} { compare_db db db2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# OOM when collecting and using a rebase changeset.
|
||||||
|
#
|
||||||
|
reset_db
|
||||||
|
do_execsql_test 2.0 {
|
||||||
|
CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c));
|
||||||
|
CREATE TABLE t4(x PRIMARY KEY, y, z);
|
||||||
|
|
||||||
|
INSERT INTO t3 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t3 VALUES(4, 2, 5);
|
||||||
|
INSERT INTO t3 VALUES(7, 2, 9);
|
||||||
|
|
||||||
|
INSERT INTO t4 VALUES('a', 'b', 'c');
|
||||||
|
INSERT INTO t4 VALUES('d', 'e', 'f');
|
||||||
|
INSERT INTO t4 VALUES('g', 'h', 'i');
|
||||||
|
}
|
||||||
|
faultsim_save_and_close
|
||||||
|
|
||||||
|
proc xConflict {ret args} { return $ret }
|
||||||
|
|
||||||
|
do_test 2.1 {
|
||||||
|
faultsim_restore_and_reopen
|
||||||
|
set C1 [changeset_from_sql {
|
||||||
|
INSERT INTO t3 VALUES(10, 11, 12);
|
||||||
|
UPDATE t4 SET y='j' WHERE x='g';
|
||||||
|
DELETE FROM t4 WHERE x='a';
|
||||||
|
}]
|
||||||
|
|
||||||
|
faultsim_restore_and_reopen
|
||||||
|
set C2 [changeset_from_sql {
|
||||||
|
INSERT INTO t3 VALUES(1000, 11, 12);
|
||||||
|
DELETE FROM t4 WHERE x='g';
|
||||||
|
}]
|
||||||
|
|
||||||
|
faultsim_restore_and_reopen
|
||||||
|
sqlite3changeset_apply db $C1 [list xConflict OMIT]
|
||||||
|
faultsim_save_and_close
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_faultsim_test 2.2 -faults oom* -prep {
|
||||||
|
catch {db2 close}
|
||||||
|
catch {db close}
|
||||||
|
faultsim_restore_and_reopen
|
||||||
|
sqlite3 db2 test.db2
|
||||||
|
} -body {
|
||||||
|
set rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict OMIT]]
|
||||||
|
set {} {}
|
||||||
|
} -test {
|
||||||
|
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||||
|
}
|
||||||
|
do_faultsim_test 2.3 -faults oom* -prep {
|
||||||
|
catch {db2 close}
|
||||||
|
catch {db close}
|
||||||
|
faultsim_restore_and_reopen
|
||||||
|
sqlite3 db2 test.db2
|
||||||
|
} -body {
|
||||||
|
set rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict REPLACE]]
|
||||||
|
set {} {}
|
||||||
|
} -test {
|
||||||
|
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||||
|
}
|
||||||
|
do_faultsim_test 2.4 -faults oom* -prep {
|
||||||
|
catch {db2 close}
|
||||||
|
catch {db close}
|
||||||
|
faultsim_restore_and_reopen
|
||||||
|
set ::rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict REPLACE]]
|
||||||
|
} -body {
|
||||||
|
sqlite3rebaser_create R
|
||||||
|
R configure $::rebase
|
||||||
|
R rebase $::C1
|
||||||
|
set {} {}
|
||||||
|
} -test {
|
||||||
|
catch { R delete }
|
||||||
|
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||||
|
}
|
||||||
|
do_faultsim_test 2.5 -faults oom* -prep {
|
||||||
|
catch {db2 close}
|
||||||
|
catch {db close}
|
||||||
|
faultsim_restore_and_reopen
|
||||||
|
set ::rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict OMIT]]
|
||||||
|
} -body {
|
||||||
|
sqlite3rebaser_create R
|
||||||
|
R configure $::rebase
|
||||||
|
R rebase $::C1
|
||||||
|
set {} {}
|
||||||
|
} -test {
|
||||||
|
catch { R delete }
|
||||||
|
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -59,6 +59,90 @@ proc do_apply_v2_test {tn sql modsql conflict_handler res} {
|
|||||||
uplevel [list do_test $tn [list changeset_to_list $blob] [list {*}$res]]
|
uplevel [list do_test $tn [list changeset_to_list $blob] [list {*}$res]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set ::lConflict [list]
|
||||||
|
proc xConflict {args} {
|
||||||
|
set res [lindex $::lConflict 0]
|
||||||
|
set ::lConflict [lrange $::lConflict 1 end]
|
||||||
|
return $res
|
||||||
|
}
|
||||||
|
|
||||||
|
# Take a copy of database test.db in file test.db2. Execute $sql1
|
||||||
|
# against test.db and $sql2 against test.db2. Capture a changeset
|
||||||
|
# for each. Then send the test.db2 changeset to test.db and apply
|
||||||
|
# it with the conflict handlers in $conflict_handler. Patch the
|
||||||
|
# test.db changeset and then execute it against test.db2. Test that
|
||||||
|
# the two databases come out the same.
|
||||||
|
#
|
||||||
|
proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} {
|
||||||
|
|
||||||
|
for {set i 1} {$i <= 2} {incr i} {
|
||||||
|
forcedelete test.db2 test.db2-journal test.db2-wal
|
||||||
|
forcecopy test.db test.db2
|
||||||
|
sqlite3 db2 test.db2
|
||||||
|
|
||||||
|
db eval BEGIN
|
||||||
|
|
||||||
|
sqlite3session S1 db main
|
||||||
|
S1 attach *
|
||||||
|
execsql $sql1 db
|
||||||
|
set c1 [S1 changeset]
|
||||||
|
S1 delete
|
||||||
|
|
||||||
|
if {$i==1} {
|
||||||
|
sqlite3session S2 db2 main
|
||||||
|
S2 attach *
|
||||||
|
execsql $sql2 db2
|
||||||
|
set c2 [S2 changeset]
|
||||||
|
S2 delete
|
||||||
|
} else {
|
||||||
|
set c2 [list]
|
||||||
|
foreach sql [split $sql2 ";"] {
|
||||||
|
if {[string is space $sql]} continue
|
||||||
|
sqlite3session S2 db2 main
|
||||||
|
S2 attach *
|
||||||
|
execsql $sql db2
|
||||||
|
lappend c2 [S2 changeset]
|
||||||
|
S2 delete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set ::lConflict $conflict_handler
|
||||||
|
set rebase [list]
|
||||||
|
if {$i==1} {
|
||||||
|
lappend rebase [sqlite3changeset_apply_v2 db $c2 xConflict]
|
||||||
|
} else {
|
||||||
|
foreach c $c2 {
|
||||||
|
lappend rebase [sqlite3changeset_apply_v2 db $c xConflict]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint }
|
||||||
|
#puts [changeset_to_list [lindex $rebase 0]] ; breakpoint
|
||||||
|
#puts [llength $rebase]
|
||||||
|
|
||||||
|
if {$i==2 && $tn=="3.3.1"} breakpoint
|
||||||
|
sqlite3rebaser_create R
|
||||||
|
foreach r $rebase {
|
||||||
|
puts [changeset_to_list $r]
|
||||||
|
R configure $r
|
||||||
|
}
|
||||||
|
set c1r [R rebase $c1]
|
||||||
|
R delete
|
||||||
|
#if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] }
|
||||||
|
|
||||||
|
sqlite3changeset_apply_v2 db2 $c1r xConflictAbort
|
||||||
|
|
||||||
|
uplevel [list do_test $tn.$i.1 [list compare_db db db2] {}]
|
||||||
|
db2 close
|
||||||
|
|
||||||
|
if {$testsql!=""} {
|
||||||
|
uplevel [list do_execsql_test $tn.$i.2 $testsql $testres]
|
||||||
|
}
|
||||||
|
|
||||||
|
db eval ROLLBACK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
do_execsql_test 1.0 {
|
do_execsql_test 1.0 {
|
||||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||||
INSERT INTO t1 VALUES(1, 'value A');
|
INSERT INTO t1 VALUES(1, 'value A');
|
||||||
@ -141,55 +225,6 @@ proc xConflictAbort {args} {
|
|||||||
return "ABORT"
|
return "ABORT"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Take a copy of database test.db in file test.db2. Execute $sql1
|
|
||||||
# against test.db and $sql2 against test.db2. Capture a changeset
|
|
||||||
# for each. Then send the test.db2 changeset to test.db and apply
|
|
||||||
# it with the conflict handlers in $conflict_handler. Patch the
|
|
||||||
# test.db changeset and then execute it against test.db2. Test that
|
|
||||||
# the two databases come out the same.
|
|
||||||
#
|
|
||||||
proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} {
|
|
||||||
|
|
||||||
forcedelete test.db2 test.db2-journal test.db2-wal
|
|
||||||
forcecopy test.db test.db2
|
|
||||||
sqlite3 db2 test.db2
|
|
||||||
|
|
||||||
db eval BEGIN
|
|
||||||
|
|
||||||
sqlite3session S1 db main
|
|
||||||
S1 attach *
|
|
||||||
execsql $sql1 db
|
|
||||||
set c1 [S1 changeset]
|
|
||||||
S1 delete
|
|
||||||
|
|
||||||
sqlite3session S2 db2 main
|
|
||||||
S2 attach *
|
|
||||||
execsql $sql2 db2
|
|
||||||
set c2 [S2 changeset]
|
|
||||||
S2 delete
|
|
||||||
|
|
||||||
set ::lConflict $conflict_handler
|
|
||||||
set rebase [sqlite3changeset_apply_v2 db $c2 xConflict]
|
|
||||||
#if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint }
|
|
||||||
|
|
||||||
sqlite3rebaser_create R
|
|
||||||
R configure $rebase
|
|
||||||
set c1r [R rebase $c1]
|
|
||||||
R delete
|
|
||||||
#if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] }
|
|
||||||
|
|
||||||
sqlite3changeset_apply_v2 db2 $c1r xConflictAbort
|
|
||||||
|
|
||||||
uplevel [list do_test $tn.1 [list compare_db db db2] {}]
|
|
||||||
db2 close
|
|
||||||
|
|
||||||
if {$testsql!=""} {
|
|
||||||
uplevel [list do_execsql_test $tn.2 $testsql $testres]
|
|
||||||
}
|
|
||||||
|
|
||||||
db eval ROLLBACK
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_db
|
reset_db
|
||||||
do_execsql_test 2.1.0 {
|
do_execsql_test 2.1.0 {
|
||||||
CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);
|
CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);
|
||||||
@ -198,7 +233,7 @@ do_execsql_test 2.1.0 {
|
|||||||
INSERT INTO t1 VALUES(3, 'three');
|
INSERT INTO t1 VALUES(3, 'three');
|
||||||
}
|
}
|
||||||
do_rebase_test 2.1.1 {
|
do_rebase_test 2.1.1 {
|
||||||
UPDATE t1 SET b = 'two.1' WHERE a=2;
|
UPDATE t1 SET b = 'two.1' WHERE a=2
|
||||||
} {
|
} {
|
||||||
UPDATE t1 SET b = 'two.2' WHERE a=2;
|
UPDATE t1 SET b = 'two.2' WHERE a=2;
|
||||||
} {
|
} {
|
||||||
@ -206,7 +241,7 @@ do_rebase_test 2.1.1 {
|
|||||||
} { SELECT * FROM t1 } {1 one 2 two.1 3 three}
|
} { SELECT * FROM t1 } {1 one 2 two.1 3 three}
|
||||||
|
|
||||||
do_rebase_test 2.1.2 {
|
do_rebase_test 2.1.2 {
|
||||||
UPDATE t1 SET b = 'two.1' WHERE a=2;
|
UPDATE t1 SET b = 'two.1' WHERE a=2
|
||||||
} {
|
} {
|
||||||
UPDATE t1 SET b = 'two.2' WHERE a=2;
|
UPDATE t1 SET b = 'two.2' WHERE a=2;
|
||||||
} {
|
} {
|
||||||
@ -214,7 +249,7 @@ do_rebase_test 2.1.2 {
|
|||||||
} { SELECT * FROM t1 } {1 one 2 two.2 3 three}
|
} { SELECT * FROM t1 } {1 one 2 two.2 3 three}
|
||||||
|
|
||||||
do_rebase_test 2.1.3 {
|
do_rebase_test 2.1.3 {
|
||||||
DELETE FROM t1 WHERE a=3;
|
DELETE FROM t1 WHERE a=3
|
||||||
} {
|
} {
|
||||||
DELETE FROM t1 WHERE a=3;
|
DELETE FROM t1 WHERE a=3;
|
||||||
} {
|
} {
|
||||||
@ -222,7 +257,7 @@ do_rebase_test 2.1.3 {
|
|||||||
} { SELECT * FROM t1 } {1 one 2 two}
|
} { SELECT * FROM t1 } {1 one 2 two}
|
||||||
|
|
||||||
do_rebase_test 2.1.4 {
|
do_rebase_test 2.1.4 {
|
||||||
DELETE FROM t1 WHERE a=1;
|
DELETE FROM t1 WHERE a=1
|
||||||
} {
|
} {
|
||||||
UPDATE t1 SET b='one.2' WHERE a=1
|
UPDATE t1 SET b='one.2' WHERE a=1
|
||||||
} {
|
} {
|
||||||
@ -238,7 +273,7 @@ do_rebase_test 2.1.4 {
|
|||||||
#} { SELECT * FROM t1 } {2 two 3 three}
|
#} { SELECT * FROM t1 } {2 two 3 three}
|
||||||
|
|
||||||
do_rebase_test 2.1.6 {
|
do_rebase_test 2.1.6 {
|
||||||
UPDATE t1 SET b='three.1' WHERE a=3;
|
UPDATE t1 SET b='three.1' WHERE a=3
|
||||||
} {
|
} {
|
||||||
DELETE FROM t1 WHERE a=3;
|
DELETE FROM t1 WHERE a=3;
|
||||||
} {
|
} {
|
||||||
@ -246,7 +281,7 @@ do_rebase_test 2.1.6 {
|
|||||||
} { SELECT * FROM t1 } {1 one 2 two 3 three.1}
|
} { SELECT * FROM t1 } {1 one 2 two 3 three.1}
|
||||||
|
|
||||||
do_rebase_test 2.1.7 {
|
do_rebase_test 2.1.7 {
|
||||||
UPDATE t1 SET b='three.1' WHERE a=3;
|
UPDATE t1 SET b='three.1' WHERE a=3
|
||||||
} {
|
} {
|
||||||
DELETE FROM t1 WHERE a=3;
|
DELETE FROM t1 WHERE a=3;
|
||||||
} {
|
} {
|
||||||
@ -254,7 +289,7 @@ do_rebase_test 2.1.7 {
|
|||||||
} { SELECT * FROM t1 } {1 one 2 two}
|
} { SELECT * FROM t1 } {1 one 2 two}
|
||||||
|
|
||||||
do_rebase_test 2.1.8 {
|
do_rebase_test 2.1.8 {
|
||||||
INSERT INTO t1 VALUES(4, 'four.1');
|
INSERT INTO t1 VALUES(4, 'four.1')
|
||||||
} {
|
} {
|
||||||
INSERT INTO t1 VALUES(4, 'four.2');
|
INSERT INTO t1 VALUES(4, 'four.2');
|
||||||
} {
|
} {
|
||||||
@ -262,7 +297,7 @@ do_rebase_test 2.1.8 {
|
|||||||
} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2}
|
} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2}
|
||||||
|
|
||||||
do_rebase_test 2.1.9 {
|
do_rebase_test 2.1.9 {
|
||||||
INSERT INTO t1 VALUES(4, 'four.1');
|
INSERT INTO t1 VALUES(4, 'four.1')
|
||||||
} {
|
} {
|
||||||
INSERT INTO t1 VALUES(4, 'four.2');
|
INSERT INTO t1 VALUES(4, 'four.2');
|
||||||
} {
|
} {
|
||||||
@ -281,14 +316,14 @@ do_execsql_test 2.2.0 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do_rebase_test 2.2.1 {
|
do_rebase_test 2.2.1 {
|
||||||
UPDATE t2 SET x=1 WHERE z='A';
|
UPDATE t2 SET x=1 WHERE z='A'
|
||||||
} {
|
} {
|
||||||
UPDATE t2 SET y='one' WHERE z='A';
|
UPDATE t2 SET y='one' WHERE z='A';
|
||||||
} {
|
} {
|
||||||
} { SELECT * FROM t2 WHERE z='A' } { 1 one A }
|
} { SELECT * FROM t2 WHERE z='A' } { 1 one A }
|
||||||
|
|
||||||
do_rebase_test 2.2.2 {
|
do_rebase_test 2.2.2 {
|
||||||
UPDATE t2 SET x=1, y='one' WHERE z='B';
|
UPDATE t2 SET x=1, y='one' WHERE z='B'
|
||||||
} {
|
} {
|
||||||
UPDATE t2 SET y='two' WHERE z='B';
|
UPDATE t2 SET y='two' WHERE z='B';
|
||||||
} {
|
} {
|
||||||
@ -296,12 +331,54 @@ do_rebase_test 2.2.2 {
|
|||||||
} { SELECT * FROM t2 WHERE z='B' } { 1 two B }
|
} { SELECT * FROM t2 WHERE z='B' } { 1 two B }
|
||||||
|
|
||||||
do_rebase_test 2.2.3 {
|
do_rebase_test 2.2.3 {
|
||||||
UPDATE t2 SET x=1, y='one' WHERE z='B';
|
UPDATE t2 SET x=1, y='one' WHERE z='B'
|
||||||
} {
|
} {
|
||||||
UPDATE t2 SET y='two' WHERE z='B';
|
UPDATE t2 SET y='two' WHERE z='B';
|
||||||
} {
|
} {
|
||||||
OMIT
|
OMIT
|
||||||
} { SELECT * FROM t2 WHERE z='B' } { 1 one B }
|
} { SELECT * FROM t2 WHERE z='B' } { 1 one B }
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
reset_db
|
||||||
|
do_execsql_test 3.0 {
|
||||||
|
CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c));
|
||||||
|
CREATE TABLE abcdefghijkl(x PRIMARY KEY, y, z);
|
||||||
|
|
||||||
|
INSERT INTO t3 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t3 VALUES(4, 2, 5);
|
||||||
|
INSERT INTO t3 VALUES(7, 2, 9);
|
||||||
|
|
||||||
|
INSERT INTO abcdefghijkl VALUES('a', 'b', 'c');
|
||||||
|
INSERT INTO abcdefghijkl VALUES('d', 'e', 'f');
|
||||||
|
INSERT INTO abcdefghijkl VALUES('g', 'h', 'i');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach {tn p} {
|
||||||
|
1 OMIT 2 REPLACE
|
||||||
|
} {
|
||||||
|
do_rebase_test 3.1.$tn {
|
||||||
|
INSERT INTO t3 VALUES(1, 1, 1);
|
||||||
|
UPDATE abcdefghijkl SET y=2;
|
||||||
|
} {
|
||||||
|
INSERT INTO t3 VALUES(4, 1, 1);
|
||||||
|
DELETE FROM abcdefghijkl;
|
||||||
|
} [list $p $p $p $p $p $p $p $p]
|
||||||
|
|
||||||
|
do_rebase_test 3.2.$tn {
|
||||||
|
INSERT INTO abcdefghijkl SELECT * FROM t3;
|
||||||
|
UPDATE t3 SET b=b+1;
|
||||||
|
} {
|
||||||
|
INSERT INTO t3 VALUES(3, 3, 3);
|
||||||
|
INSERT INTO abcdefghijkl SELECT * FROM t3;
|
||||||
|
} [list $p $p $p $p $p $p $p $p]
|
||||||
|
|
||||||
|
do_rebase_test 3.3.$tn {
|
||||||
|
INSERT INTO abcdefghijkl VALUES(22, 23, 24);
|
||||||
|
} {
|
||||||
|
INSERT INTO abcdefghijkl VALUES(22, 25, 26);
|
||||||
|
UPDATE abcdefghijkl SET y=400 WHERE x=22;
|
||||||
|
} [list $p $p $p $p $p $p $p $p]
|
||||||
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -596,7 +596,7 @@ static int sessionChangeEqual(
|
|||||||
int n1 = sessionSerialLen(a1);
|
int n1 = sessionSerialLen(a1);
|
||||||
int n2 = sessionSerialLen(a2);
|
int n2 = sessionSerialLen(a2);
|
||||||
|
|
||||||
if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){
|
if( n1!=n2 || memcmp(a1, a2, n1) ){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
a1 += n1;
|
a1 += n1;
|
||||||
@ -2183,6 +2183,7 @@ static int sessionSelectStmt(
|
|||||||
"SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND "
|
"SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND "
|
||||||
"idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb
|
"idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb
|
||||||
);
|
);
|
||||||
|
if( zSql==0 ) rc = SQLITE_NOMEM;
|
||||||
}else{
|
}else{
|
||||||
int i;
|
int i;
|
||||||
const char *zSep = "";
|
const char *zSep = "";
|
||||||
@ -4539,7 +4540,12 @@ static int sessionChangeMerge(
|
|||||||
pNew->aRecord = (u8*)&pNew[1];
|
pNew->aRecord = (u8*)&pNew[1];
|
||||||
memcpy(pNew->aRecord, aRec, nRec);
|
memcpy(pNew->aRecord, aRec, nRec);
|
||||||
}else if( bRebase){
|
}else if( bRebase){
|
||||||
assert( 0 );
|
/*
|
||||||
|
** op1=INSERT/R, op2=INSERT/R ->
|
||||||
|
** op1=INSERT/R, op2=INSERT/O ->
|
||||||
|
** op1=INSERT/O, op2=INSERT/R ->
|
||||||
|
** op1=INSERT/O, op2=INSERT/O ->
|
||||||
|
*/
|
||||||
}else{
|
}else{
|
||||||
int op1 = pExist->op;
|
int op1 = pExist->op;
|
||||||
|
|
||||||
@ -5169,59 +5175,6 @@ static int sessionRebase(
|
|||||||
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
||||||
sessionAppendBlob(&sOut, aRec, nRec, &rc);
|
sessionAppendBlob(&sOut, aRec, nRec, &rc);
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
/* If pChange is an INSERT, then rebase the change. If it is a
|
|
||||||
** DELETE, omit the change from the output altogether. */
|
|
||||||
if( pChange->op==SQLITE_INSERT ){
|
|
||||||
if( pChange->bIndirect ){
|
|
||||||
/* The change being rebased against was a DELETE. So, if the
|
|
||||||
** input is a:
|
|
||||||
**
|
|
||||||
** DELETE - omit the change altogether.
|
|
||||||
** UPDATE - change to an INSERT,
|
|
||||||
** INSERT - no change (output the record as is).
|
|
||||||
*/
|
|
||||||
if( pIter->op!=SQLITE_DELETE ){
|
|
||||||
sessionAppendByte(&sOut, SQLITE_INSERT, &rc);
|
|
||||||
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
|
||||||
if( pIter->op==SQLITE_INSERT ){
|
|
||||||
sessionAppendBlob(&sOut, aRec, nRec, &rc);
|
|
||||||
}else{
|
|
||||||
u8 *pCsr = aRec;
|
|
||||||
sessionSkipRecord(&pCsr, pIter->nCol);
|
|
||||||
sessionAppendRecordMerge(&sOut, pIter->nCol, 1,
|
|
||||||
pCsr, nRec-(pCsr-aRec),
|
|
||||||
pChange->aRecord, pChange->nRecord, &rc
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if( pIter->op==SQLITE_INSERT ){
|
|
||||||
sessionAppendByte(&sOut, SQLITE_UPDATE, &rc);
|
|
||||||
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
|
||||||
sessionAppendBlob(&sOut, pChange->aRecord, pChange->nRecord, &rc);
|
|
||||||
sessionAppendBlob(&sOut, aRec, nRec, &rc);
|
|
||||||
}else{
|
|
||||||
u8 *pCsr = aRec;
|
|
||||||
sessionAppendByte(&sOut, pIter->op, &rc);
|
|
||||||
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
|
||||||
sessionAppendRecordMerge(&sOut, pIter->nCol, 0,
|
|
||||||
aRec, nRec, pChange->aRecord, pChange->nRecord, &rc
|
|
||||||
);
|
|
||||||
if( pIter->op==SQLITE_UPDATE ){
|
|
||||||
sessionSkipRecord(&pCsr, pIter->nCol);
|
|
||||||
sessionAppendBlob(&sOut, pCsr, nRec - (pCsr-aRec), &rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
sessionAppendByte(&sOut, pIter->op, &rc);
|
|
||||||
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
|
||||||
sessionAppendBlob(&sOut, aRec, nRec, &rc);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){
|
if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){
|
||||||
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
|
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
|
||||||
sOut.nBuf = 0;
|
sOut.nBuf = 0;
|
||||||
|
@ -1219,27 +1219,51 @@ int sqlite3changeset_apply_v2(
|
|||||||
/*
|
/*
|
||||||
** CAPI3REF: Rebasing changesets
|
** CAPI3REF: Rebasing changesets
|
||||||
**
|
**
|
||||||
** Changes are rebased as follows:
|
** Suppose there is a site hosting a database in state S0. And that
|
||||||
|
** modifications are made that move that database to state S1 and a
|
||||||
|
** changeset recorded (the "local" changeset). Then, a changeset based
|
||||||
|
** on S0 is received from another site (the "remote" changeset) and
|
||||||
|
** applied to the database. The database is then in state
|
||||||
|
** (S1+"remote"), where the exact state depends on any conflict
|
||||||
|
** resolution decisions (OMIT or REPLACE) made while applying "remote".
|
||||||
|
** Rebasing a changeset is to update it to take those conflict
|
||||||
|
** resolution decisions into account, so that the same conflicts
|
||||||
|
** do not have to be resolved elsewhere in the network.
|
||||||
|
**
|
||||||
|
** For example, if both the local and remote changesets contain an
|
||||||
|
** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)":
|
||||||
|
**
|
||||||
|
** local: INSERT INTO t1 VALUES(1, 'v1');
|
||||||
|
** remote: INSERT INTO t1 VALUES(1, 'v2');
|
||||||
|
**
|
||||||
|
** and the conflict resolution is REPLACE, then the INSERT change is
|
||||||
|
** removed from the local changeset (it was overridden). Or, if the
|
||||||
|
** conflict resolution was "OMIT", then the local changeset is modified
|
||||||
|
** to instead contain:
|
||||||
|
**
|
||||||
|
** UPDATE t1 SET b = 'v2' WHERE a=1;
|
||||||
|
**
|
||||||
|
** Changes within the local changeset are rebased as follows:
|
||||||
**
|
**
|
||||||
** <dl>
|
** <dl>
|
||||||
** <dt>INSERT<dd>
|
** <dt>Local INSERT<dd>
|
||||||
** This may only conflict with a remote INSERT. If the conflict
|
** This may only conflict with a remote INSERT. If the conflict
|
||||||
** resolution was OMIT, then add an UPDATE change to the rebased
|
** resolution was OMIT, then add an UPDATE change to the rebased
|
||||||
** changeset. Or, if the conflict resolution was REPLACE, add
|
** changeset. Or, if the conflict resolution was REPLACE, add
|
||||||
** nothing to the rebased changeset.
|
** nothing to the rebased changeset.
|
||||||
**
|
**
|
||||||
** <dt>DELETE<dd>
|
** <dt>Local DELETE<dd>
|
||||||
** This may conflict with a remote UPDATE or DELETE. In both cases the
|
** This may conflict with a remote UPDATE or DELETE. In both cases the
|
||||||
** only possible resolution is OMIT. If the remote operation was a
|
** only possible resolution is OMIT. If the remote operation was a
|
||||||
** DELETE, then add no change to the rebased changeset. If the remote
|
** DELETE, then add no change to the rebased changeset. If the remote
|
||||||
** operation was an UPDATE, then the old.* fields of the are updated to
|
** operation was an UPDATE, then the old.* fields of change are updated
|
||||||
** reflect the new.* values in the UPDATE.
|
** to reflect the new.* values in the UPDATE.
|
||||||
**
|
**
|
||||||
** <dt>UPDATE<dd>
|
** <dt>Local UPDATE<dd>
|
||||||
** This may conflict with a remote UPDATE or DELETE. If it conflicts
|
** This may conflict with a remote UPDATE or DELETE. If it conflicts
|
||||||
** with a DELETE, and the conflict resolution was OMIT, then the update
|
** with a DELETE, and the conflict resolution was OMIT, then the update
|
||||||
** is changed into an INSERT. Any undefined values in the new.* record
|
** is changed into an INSERT. Any undefined values in the new.* record
|
||||||
** from the update change are filled in using hte old.* values from
|
** from the update change are filled in using the old.* values from
|
||||||
** the conflicting DELETE. Or, if the conflict resolution was REPLACE,
|
** the conflicting DELETE. Or, if the conflict resolution was REPLACE,
|
||||||
** the UPDATE change is simply omitted from the rebased changeset.
|
** the UPDATE change is simply omitted from the rebased changeset.
|
||||||
**
|
**
|
||||||
@ -1250,6 +1274,9 @@ int sqlite3changeset_apply_v2(
|
|||||||
** the conflicting UPDATE removed. If this means no columns would be
|
** the conflicting UPDATE removed. If this means no columns would be
|
||||||
** updated, the change is omitted.
|
** updated, the change is omitted.
|
||||||
** </dl>
|
** </dl>
|
||||||
|
**
|
||||||
|
** A local change may be rebased against multiple remote changes
|
||||||
|
** simultaneously.
|
||||||
*/
|
*/
|
||||||
typedef struct sqlite3_rebaser sqlite3_rebaser;
|
typedef struct sqlite3_rebaser sqlite3_rebaser;
|
||||||
|
|
||||||
|
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
|||||||
C Fix\sa\sproblem\swith\shandling\srebasing\sUPDATE\schanges\sfor\sREPLACE\sconflict\nresolution.
|
C Add\sfurther\stests\sand\sdocumentation\sfor\sthe\ssessions\srebase\sfeature.
|
||||||
D 2018-03-16T18:02:47.093
|
D 2018-03-20T20:27:03.438
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
|
F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
|
||||||
@ -394,17 +394,17 @@ F ext/session/sessionE.test 0a616c4ad8fd2c05f23217ebb6212ef80b7fef30f5f086a6633a
|
|||||||
F ext/session/sessionF.test c2f178d4dfd723a5fd94a730ea2ccb44c669e3ce
|
F ext/session/sessionF.test c2f178d4dfd723a5fd94a730ea2ccb44c669e3ce
|
||||||
F ext/session/sessionG.test 63f9a744341d670775af29e4f19c1ef09a4810798400f28cd76704803a2e56ff
|
F ext/session/sessionG.test 63f9a744341d670775af29e4f19c1ef09a4810798400f28cd76704803a2e56ff
|
||||||
F ext/session/sessionH.test 332b60e4c2e0a680105e11936201cabe378216f307e2747803cea56fa7d9ebae
|
F ext/session/sessionH.test 332b60e4c2e0a680105e11936201cabe378216f307e2747803cea56fa7d9ebae
|
||||||
F ext/session/session_common.tcl 7776eda579773113b30c7abfd4545c445228cb73
|
F ext/session/session_common.tcl 748141b02042b942e04a7afad9ffb2212a3997de536ed95f6dec7bb5018ede2c
|
||||||
F ext/session/session_speed_test.c edc1f96fd5e0e4b16eb03e2a73041013d59e8723
|
F ext/session/session_speed_test.c edc1f96fd5e0e4b16eb03e2a73041013d59e8723
|
||||||
F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28f0c1cc142c3ec
|
F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28f0c1cc142c3ec
|
||||||
F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
|
F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
|
||||||
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
|
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
|
||||||
F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0
|
F ext/session/sessionfault2.test 883c8919ebcf6c140ba86b480bc14ae642ee9969c009e0b355c8981a5266f9ed
|
||||||
F ext/session/sessionrebase.test 35dede926077b0bbb5c323e278ed575a24fa333b3c2f45f3b6842b532054e463
|
F ext/session/sessionrebase.test a150289bf25176f14983fbd519cdd97921fd52de682d0c75849f44daf51d37e4
|
||||||
F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e
|
F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e
|
||||||
F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc
|
F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc
|
||||||
F ext/session/sqlite3session.c 84f4786c93b12701cf72073064ea70740a7d43508f0bfb96563c49cfd7df644c
|
F ext/session/sqlite3session.c b51365d4fe085409bab2e19d7c5f796a3ac6c5e205b0ac3e409dad4e8b9df1b8
|
||||||
F ext/session/sqlite3session.h a1c66a6497c36246d2242223663c2e7c1906bd28099556d7e5148214c2d9902a
|
F ext/session/sqlite3session.h cc09a873386bdb95079746f17e2c8d7261a11fab6a01e52fc1c8237adfa5a145
|
||||||
F ext/session/test_session.c f253742ea01b089326f189b5ae15a5b55c1c9e97452e4a195ee759ba51b404d5
|
F ext/session/test_session.c f253742ea01b089326f189b5ae15a5b55c1c9e97452e4a195ee759ba51b404d5
|
||||||
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||||
@ -1713,7 +1713,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P cf0d1abb44cf170d747e9c11f49ec03a29f00ab4821c613ca1e05b883a568211
|
P f7bf71f1d47044e3cbc74018294b8af5ad52c2bb84954e99bbd4e9b8c36fc077
|
||||||
R 120c1710087879bab403c404357c60c1
|
R 09a1cb643bb8175d8449dc1dfe2a3ce4
|
||||||
U dan
|
U dan
|
||||||
Z 77da19287ec96a71252949a7a74af667
|
Z deee606c0fac26e4bea6293ad6f81334
|
||||||
|
@ -1 +1 @@
|
|||||||
f7bf71f1d47044e3cbc74018294b8af5ad52c2bb84954e99bbd4e9b8c36fc077
|
7475a363ebb272ae23c0796fe7587714a156dc6a3a4a57ed948ed6f69d3c1218
|
Reference in New Issue
Block a user