1# 2018 March 14
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# This file implements regression tests for SQLite library.
12#
13
14if {![info exists testdir]} {
15  set testdir [file join [file dirname [info script]] .. .. test]
16}
17source [file join [file dirname [info script]] session_common.tcl]
18source $testdir/tester.tcl
19ifcapable !session {finish_test; return}
20
21set testprefix sessionrebase
22
23set ::lConflict [list]
24proc xConflict {args} {
25  set res [lindex $::lConflict 0]
26  set ::lConflict [lrange $::lConflict 1 end]
27  return $res
28}
29
30#-------------------------------------------------------------------------
31# The following test cases - 1.* - test that the rebase blobs output by
32# sqlite3_changeset_apply_v2 look correct in some simple cases. The blob
33# is itself a changeset, containing records determined as follows:
34#
35#   * For each conflict resolved with REPLACE, the rebase blob contains
36#     a DELETE record. All fields other than the PK fields are undefined.
37#
38#   * For each conflict resolved with OMIT, the rebase blob contains an
39#     INSERT record. For an INSERT or UPDATE operation, the indirect flag
40#     is clear and all updated fields are defined. For a DELETE operation,
41#     the indirect flag is set and all non-PK fields left undefined.
42#
43proc do_apply_v2_test {tn sql modsql conflict_handler res} {
44
45  execsql BEGIN
46  sqlite3session S db main
47  S attach *
48  execsql $sql
49  set changeset [S changeset]
50  S delete
51  execsql ROLLBACK
52
53  execsql BEGIN
54  execsql $modsql
55  set ::lConflict $conflict_handler
56  set blob [sqlite3changeset_apply_v2 db $changeset xConflict]
57  execsql ROLLBACK
58
59  uplevel [list do_test $tn [list changeset_to_list $blob] [list {*}$res]]
60}
61
62do_execsql_test 1.0 {
63  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
64  INSERT INTO t1 VALUES(1, 'value A');
65}
66
67do_apply_v2_test 1.1.1 {
68  UPDATE t1 SET b = 'value B' WHERE a=1;
69} {
70  UPDATE t1 SET b = 'value C' WHERE a=1;
71} {
72  OMIT
73} {
74  {INSERT t1 0 X. {} {i 1 t {value B}}}
75}
76
77do_apply_v2_test 1.1.2 {
78  UPDATE t1 SET b = 'value B' WHERE a=1;
79} {
80  UPDATE t1 SET b = 'value C' WHERE a=1;
81} {
82  REPLACE
83} {
84  {INSERT t1 1 X. {} {i 1 t {value B}}}
85}
86
87do_apply_v2_test 1.2.1 {
88  INSERT INTO t1 VALUES(2, 'first');
89} {
90  INSERT INTO t1 VALUES(2, 'second');
91} {
92  OMIT
93} {
94  {INSERT t1 0 X. {} {i 2 t first}}
95}
96do_apply_v2_test 1.2.2 {
97  INSERT INTO t1 VALUES(2, 'first');
98} {
99  INSERT INTO t1 VALUES(2, 'second');
100} {
101  REPLACE
102} {
103  {INSERT t1 1 X. {} {i 2 t first}}
104}
105
106do_apply_v2_test 1.3.1 {
107  DELETE FROM t1 WHERE a=1;
108} {
109  UPDATE t1 SET b='value D' WHERE a=1;
110} {
111  OMIT
112} {
113  {DELETE t1 0 X. {i 1 t {value A}} {}}
114}
115do_apply_v2_test 1.3.2 {
116  DELETE FROM t1 WHERE a=1;
117} {
118  UPDATE t1 SET b='value D' WHERE a=1;
119} {
120  REPLACE
121} {
122  {DELETE t1 1 X. {i 1 t {value A}} {}}
123}
124
125#-------------------------------------------------------------------------
126# Test cases 2.* - simple tests of rebasing actual changesets.
127#
128#    2.1.1 - 1u2u1r
129#    2.1.2 - 1u2u2r
130#    2.1.3 - 1d2d
131#    2.1.4 - 1d2u1r
132#    2.1.5 - 1d2u2r !!
133#    2.1.6 - 1u2d1r
134#    2.1.7 - 1u2d2r
135#
136#    2.1.8 - 1i2i2r
137#    2.1.9 - 1i2i1r
138#
139
140proc xConflictAbort {args} {
141  return "ABORT"
142}
143
144# Take a copy of database test.db in file test.db2. Execute $sql1
145# against test.db and $sql2 against test.db2. Capture a changeset
146# for each. Then send the test.db2 changeset to test.db and apply
147# it with the conflict handlers in $conflict_handler. Patch the
148# test.db changeset and then execute it against test.db2. Test that
149# the two databases come out the same.
150#
151proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} {
152
153  forcedelete test.db2 test.db2-journal test.db2-wal
154  forcecopy test.db test.db2
155  sqlite3 db2 test.db2
156
157  db eval BEGIN
158
159  sqlite3session S1 db main
160  S1 attach *
161  execsql $sql1 db
162  set c1 [S1 changeset]
163  S1 delete
164
165  sqlite3session S2 db2 main
166  S2 attach *
167  execsql $sql2 db2
168  set c2 [S2 changeset]
169  S2 delete
170
171  set ::lConflict $conflict_handler
172  set rebase [sqlite3changeset_apply_v2 db $c2 xConflict]
173  #if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint }
174
175  sqlite3rebaser_create R
176  R configure $rebase
177  set c1r [R rebase $c1]
178  R delete
179  #if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] }
180
181  sqlite3changeset_apply_v2 db2 $c1r xConflictAbort
182
183  uplevel [list do_test $tn.1 [list compare_db db db2] {}]
184  db2 close
185
186  if {$testsql!=""} {
187    uplevel [list do_execsql_test $tn.2 $testsql $testres]
188  }
189
190  db eval ROLLBACK
191}
192
193reset_db
194do_execsql_test 2.1.0 {
195  CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);
196  INSERT INTO t1 VALUES(1, 'one');
197  INSERT INTO t1 VALUES(2, 'two');
198  INSERT INTO t1 VALUES(3, 'three');
199}
200do_rebase_test 2.1.1 {
201  UPDATE t1 SET b = 'two.1' WHERE a=2;
202} {
203  UPDATE t1 SET b = 'two.2' WHERE a=2;
204} {
205  OMIT
206} { SELECT * FROM t1 } {1 one 2 two.1 3 three}
207
208do_rebase_test 2.1.2 {
209  UPDATE t1 SET b = 'two.1' WHERE a=2;
210} {
211  UPDATE t1 SET b = 'two.2' WHERE a=2;
212} {
213  REPLACE
214} { SELECT * FROM t1 } {1 one 2 two.2 3 three}
215
216do_rebase_test 2.1.3 {
217  DELETE FROM t1 WHERE a=3;
218} {
219  DELETE FROM t1 WHERE a=3;
220} {
221  OMIT
222} { SELECT * FROM t1 } {1 one 2 two}
223
224do_rebase_test 2.1.4 {
225  DELETE FROM t1 WHERE a=1;
226} {
227  UPDATE t1 SET b='one.2' WHERE a=1
228} {
229  OMIT
230} { SELECT * FROM t1 } {2 two 3 three}
231
232#do_rebase_test 2.1.5 {
233#  DELETE FROM t1 WHERE a=1;
234#} {
235#  UPDATE t1 SET b='one.2' WHERE a=1
236#} {
237#  REPLACE
238#} { SELECT * FROM t1 } {2 two 3 three}
239
240do_rebase_test 2.1.6 {
241  UPDATE t1 SET b='three.1' WHERE a=3;
242} {
243  DELETE FROM t1 WHERE a=3;
244} {
245  OMIT
246} { SELECT * FROM t1 } {1 one 2 two 3 three.1}
247
248do_rebase_test 2.1.7 {
249  UPDATE t1 SET b='three.1' WHERE a=3;
250} {
251  DELETE FROM t1 WHERE a=3;
252} {
253  REPLACE
254} { SELECT * FROM t1 } {1 one 2 two}
255
256do_rebase_test 2.1.8 {
257  INSERT INTO t1 VALUES(4, 'four.1');
258} {
259  INSERT INTO t1 VALUES(4, 'four.2');
260} {
261  REPLACE
262} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2}
263
264do_rebase_test 2.1.9 {
265  INSERT INTO t1 VALUES(4, 'four.1');
266} {
267  INSERT INTO t1 VALUES(4, 'four.2');
268} {
269  OMIT
270} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.1}
271
272do_execsql_test 2.2.0 {
273  CREATE TABLE t2(x, y, z PRIMARY KEY);
274  INSERT INTO t2 VALUES('i', 'a', 'A');
275  INSERT INTO t2 VALUES('ii', 'b', 'B');
276  INSERT INTO t2 VALUES('iii', 'c', 'C');
277
278  CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c);
279  INSERT INTO t3 VALUES(-1, 'z', 'Z');
280  INSERT INTO t3 VALUES(-2, 'y', 'Y');
281}
282
283do_rebase_test 2.2.1 {
284  UPDATE t2 SET x=1 WHERE z='A';
285} {
286  UPDATE t2 SET y='one' WHERE z='A';
287} {
288} { SELECT * FROM t2 WHERE z='A' } { 1 one A }
289
290do_rebase_test 2.2.2 {
291  UPDATE t2 SET x=1, y='one' WHERE z='B';
292} {
293  UPDATE t2 SET y='two' WHERE z='B';
294} {
295  REPLACE
296} { SELECT * FROM t2 WHERE z='B' } { 1 two B }
297
298do_rebase_test 2.2.3 {
299  UPDATE t2 SET x=1, y='one' WHERE z='B';
300} {
301  UPDATE t2 SET y='two' WHERE z='B';
302} {
303  OMIT
304} { SELECT * FROM t2 WHERE z='B' } { 1 one B }
305
306finish_test
307
308