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
62
63set ::lConflict [list]
64proc xConflict {args} {
65  set res [lindex $::lConflict 0]
66  set ::lConflict [lrange $::lConflict 1 end]
67  return $res
68}
69
70# Take a copy of database test.db in file test.db2. Execute $sql1
71# against test.db and $sql2 against test.db2. Capture a changeset
72# for each. Then send the test.db2 changeset to test.db and apply
73# it with the conflict handlers in $conflict_handler. Patch the
74# test.db changeset and then execute it against test.db2. Test that
75# the two databases come out the same.
76#
77proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} {
78
79  for {set i 1} {$i <= 2} {incr i} {
80    forcedelete test.db2 test.db2-journal test.db2-wal
81    forcecopy test.db test.db2
82    sqlite3 db2 test.db2
83
84    db eval BEGIN
85
86    sqlite3session S1 db main
87    S1 attach *
88    execsql $sql1 db
89    set c1 [S1 changeset]
90    S1 delete
91
92    if {$i==1} {
93      sqlite3session S2 db2 main
94      S2 attach *
95      execsql $sql2 db2
96      set c2 [S2 changeset]
97      S2 delete
98    } else {
99      set c2 [list]
100      foreach sql [split $sql2 ";"] {
101        if {[string is space $sql]} continue
102        sqlite3session S2 db2 main
103        S2 attach *
104        execsql $sql db2
105        lappend c2 [S2 changeset]
106        S2 delete
107      }
108    }
109
110    set ::lConflict $conflict_handler
111    set rebase [list]
112    if {$i==1} {
113      lappend rebase [sqlite3changeset_apply_v2 db $c2 xConflict]
114    } else {
115      foreach c $c2 {
116        lappend rebase [sqlite3changeset_apply_v2 db $c xConflict]
117      }
118    }
119    #if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint }
120    #puts [changeset_to_list [lindex $rebase 0]] ; breakpoint
121    #puts [llength $rebase]
122
123if {$i==2 && $tn=="3.3.1"} breakpoint
124    sqlite3rebaser_create R
125    foreach r $rebase {
126puts [changeset_to_list $r]
127      R configure $r
128    }
129    set c1r [R rebase $c1]
130    R delete
131    #if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] }
132
133    sqlite3changeset_apply_v2 db2 $c1r xConflictAbort
134
135    uplevel [list do_test $tn.$i.1 [list compare_db db db2] {}]
136    db2 close
137
138    if {$testsql!=""} {
139      uplevel [list do_execsql_test $tn.$i.2 $testsql $testres]
140    }
141
142    db eval ROLLBACK
143  }
144}
145
146do_execsql_test 1.0 {
147  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
148  INSERT INTO t1 VALUES(1, 'value A');
149}
150
151do_apply_v2_test 1.1.1 {
152  UPDATE t1 SET b = 'value B' WHERE a=1;
153} {
154  UPDATE t1 SET b = 'value C' WHERE a=1;
155} {
156  OMIT
157} {
158  {INSERT t1 0 X. {} {i 1 t {value B}}}
159}
160
161do_apply_v2_test 1.1.2 {
162  UPDATE t1 SET b = 'value B' WHERE a=1;
163} {
164  UPDATE t1 SET b = 'value C' WHERE a=1;
165} {
166  REPLACE
167} {
168  {INSERT t1 1 X. {} {i 1 t {value B}}}
169}
170
171do_apply_v2_test 1.2.1 {
172  INSERT INTO t1 VALUES(2, 'first');
173} {
174  INSERT INTO t1 VALUES(2, 'second');
175} {
176  OMIT
177} {
178  {INSERT t1 0 X. {} {i 2 t first}}
179}
180do_apply_v2_test 1.2.2 {
181  INSERT INTO t1 VALUES(2, 'first');
182} {
183  INSERT INTO t1 VALUES(2, 'second');
184} {
185  REPLACE
186} {
187  {INSERT t1 1 X. {} {i 2 t first}}
188}
189
190do_apply_v2_test 1.3.1 {
191  DELETE FROM t1 WHERE a=1;
192} {
193  UPDATE t1 SET b='value D' WHERE a=1;
194} {
195  OMIT
196} {
197  {DELETE t1 0 X. {i 1 t {value A}} {}}
198}
199do_apply_v2_test 1.3.2 {
200  DELETE FROM t1 WHERE a=1;
201} {
202  UPDATE t1 SET b='value D' WHERE a=1;
203} {
204  REPLACE
205} {
206  {DELETE t1 1 X. {i 1 t {value A}} {}}
207}
208
209#-------------------------------------------------------------------------
210# Test cases 2.* - simple tests of rebasing actual changesets.
211#
212#    2.1.1 - 1u2u1r
213#    2.1.2 - 1u2u2r
214#    2.1.3 - 1d2d
215#    2.1.4 - 1d2u1r
216#    2.1.5 - 1d2u2r !!
217#    2.1.6 - 1u2d1r
218#    2.1.7 - 1u2d2r
219#
220#    2.1.8 - 1i2i2r
221#    2.1.9 - 1i2i1r
222#
223
224proc xConflictAbort {args} {
225  return "ABORT"
226}
227
228reset_db
229do_execsql_test 2.1.0 {
230  CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);
231  INSERT INTO t1 VALUES(1, 'one');
232  INSERT INTO t1 VALUES(2, 'two');
233  INSERT INTO t1 VALUES(3, 'three');
234}
235do_rebase_test 2.1.1 {
236  UPDATE t1 SET b = 'two.1' WHERE a=2
237} {
238  UPDATE t1 SET b = 'two.2' WHERE a=2;
239} {
240  OMIT
241} { SELECT * FROM t1 } {1 one 2 two.1 3 three}
242
243do_rebase_test 2.1.2 {
244  UPDATE t1 SET b = 'two.1' WHERE a=2
245} {
246  UPDATE t1 SET b = 'two.2' WHERE a=2;
247} {
248  REPLACE
249} { SELECT * FROM t1 } {1 one 2 two.2 3 three}
250
251do_rebase_test 2.1.3 {
252  DELETE FROM t1 WHERE a=3
253} {
254  DELETE FROM t1 WHERE a=3;
255} {
256  OMIT
257} { SELECT * FROM t1 } {1 one 2 two}
258
259do_rebase_test 2.1.4 {
260  DELETE FROM t1 WHERE a=1
261} {
262  UPDATE t1 SET b='one.2' WHERE a=1
263} {
264  OMIT
265} { SELECT * FROM t1 } {2 two 3 three}
266
267#do_rebase_test 2.1.5 {
268#  DELETE FROM t1 WHERE a=1;
269#} {
270#  UPDATE t1 SET b='one.2' WHERE a=1
271#} {
272#  REPLACE
273#} { SELECT * FROM t1 } {2 two 3 three}
274
275do_rebase_test 2.1.6 {
276  UPDATE t1 SET b='three.1' WHERE a=3
277} {
278  DELETE FROM t1 WHERE a=3;
279} {
280  OMIT
281} { SELECT * FROM t1 } {1 one 2 two 3 three.1}
282
283do_rebase_test 2.1.7 {
284  UPDATE t1 SET b='three.1' WHERE a=3
285} {
286  DELETE FROM t1 WHERE a=3;
287} {
288  REPLACE
289} { SELECT * FROM t1 } {1 one 2 two}
290
291do_rebase_test 2.1.8 {
292  INSERT INTO t1 VALUES(4, 'four.1')
293} {
294  INSERT INTO t1 VALUES(4, 'four.2');
295} {
296  REPLACE
297} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2}
298
299do_rebase_test 2.1.9 {
300  INSERT INTO t1 VALUES(4, 'four.1')
301} {
302  INSERT INTO t1 VALUES(4, 'four.2');
303} {
304  OMIT
305} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.1}
306
307do_execsql_test 2.2.0 {
308  CREATE TABLE t2(x, y, z PRIMARY KEY);
309  INSERT INTO t2 VALUES('i', 'a', 'A');
310  INSERT INTO t2 VALUES('ii', 'b', 'B');
311  INSERT INTO t2 VALUES('iii', 'c', 'C');
312
313  CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c);
314  INSERT INTO t3 VALUES(-1, 'z', 'Z');
315  INSERT INTO t3 VALUES(-2, 'y', 'Y');
316}
317
318do_rebase_test 2.2.1 {
319  UPDATE t2 SET x=1 WHERE z='A'
320} {
321  UPDATE t2 SET y='one' WHERE z='A';
322} {
323} { SELECT * FROM t2 WHERE z='A' } { 1 one A }
324
325do_rebase_test 2.2.2 {
326  UPDATE t2 SET x=1, y='one' WHERE z='B'
327} {
328  UPDATE t2 SET y='two' WHERE z='B';
329} {
330  REPLACE
331} { SELECT * FROM t2 WHERE z='B' } { 1 two B }
332
333do_rebase_test 2.2.3 {
334  UPDATE t2 SET x=1, y='one' WHERE z='B'
335} {
336  UPDATE t2 SET y='two' WHERE z='B';
337} {
338  OMIT
339} { SELECT * FROM t2 WHERE z='B' } { 1 one B }
340
341#-------------------------------------------------------------------------
342reset_db
343do_execsql_test 3.0 {
344  CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c));
345  CREATE TABLE abcdefghijkl(x PRIMARY KEY, y, z);
346
347  INSERT INTO t3 VALUES(1, 2, 3);
348  INSERT INTO t3 VALUES(4, 2, 5);
349  INSERT INTO t3 VALUES(7, 2, 9);
350
351  INSERT INTO abcdefghijkl VALUES('a', 'b', 'c');
352  INSERT INTO abcdefghijkl VALUES('d', 'e', 'f');
353  INSERT INTO abcdefghijkl VALUES('g', 'h', 'i');
354}
355
356foreach {tn p} {
357    1 OMIT 2 REPLACE
358} {
359  do_rebase_test 3.1.$tn {
360    INSERT INTO t3 VALUES(1, 1, 1);
361    UPDATE abcdefghijkl SET y=2;
362  } {
363    INSERT INTO t3 VALUES(4, 1, 1);
364    DELETE FROM abcdefghijkl;
365  } [list $p $p $p $p $p $p $p $p]
366
367  do_rebase_test 3.2.$tn {
368    INSERT INTO abcdefghijkl SELECT * FROM t3;
369    UPDATE t3 SET b=b+1;
370  } {
371    INSERT INTO t3 VALUES(3, 3, 3);
372    INSERT INTO abcdefghijkl SELECT * FROM t3;
373  } [list $p $p $p $p $p $p $p $p]
374
375  do_rebase_test 3.3.$tn {
376    INSERT INTO abcdefghijkl VALUES(22, 23, 24);
377  } {
378    INSERT INTO abcdefghijkl VALUES(22, 25, 26);
379    UPDATE abcdefghijkl SET y=400 WHERE x=22;
380  } [list $p $p $p $p $p $p $p $p]
381}
382
383finish_test
384
385