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#puts "apply_v2: [changeset_to_list $c]"
117        lappend rebase [sqlite3changeset_apply_v2 db $c xConflict]
118      }
119      #puts "llength: [llength $rebase]"
120    }
121    #if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint }
122    #puts [changeset_to_list [lindex $rebase 0]] ; breakpoint
123    #puts [llength $rebase]
124
125    sqlite3rebaser_create R
126    foreach r $rebase {
127#puts [changeset_to_list $r]
128      R configure $r
129    }
130    set c1r [R rebase $c1]
131    R delete
132    #if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] }
133
134    sqlite3changeset_apply_v2 db2 $c1r xConflictAbort
135
136    if {[string range $tn end end]!="*"} {
137      uplevel [list do_test $tn.$i.1 [list compare_db db db2] {}]
138    }
139    db2 close
140
141    if {$testsql!=""} {
142      uplevel [list do_execsql_test $tn.$i.2 $testsql $testres]
143    }
144
145    db eval ROLLBACK
146  }
147}
148
149do_execsql_test 1.0 {
150  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
151  INSERT INTO t1 VALUES(1, 'value A');
152}
153
154do_apply_v2_test 1.1.1 {
155  UPDATE t1 SET b = 'value B' WHERE a=1;
156} {
157  UPDATE t1 SET b = 'value C' WHERE a=1;
158} {
159  OMIT
160} {
161  {INSERT t1 0 X. {} {i 1 t {value B}}}
162}
163
164do_apply_v2_test 1.1.2 {
165  UPDATE t1 SET b = 'value B' WHERE a=1;
166} {
167  UPDATE t1 SET b = 'value C' WHERE a=1;
168} {
169  REPLACE
170} {
171  {INSERT t1 1 X. {} {i 1 t {value B}}}
172}
173
174do_apply_v2_test 1.2.1 {
175  INSERT INTO t1 VALUES(2, 'first');
176} {
177  INSERT INTO t1 VALUES(2, 'second');
178} {
179  OMIT
180} {
181  {INSERT t1 0 X. {} {i 2 t first}}
182}
183do_apply_v2_test 1.2.2 {
184  INSERT INTO t1 VALUES(2, 'first');
185} {
186  INSERT INTO t1 VALUES(2, 'second');
187} {
188  REPLACE
189} {
190  {INSERT t1 1 X. {} {i 2 t first}}
191}
192
193do_apply_v2_test 1.3.1 {
194  DELETE FROM t1 WHERE a=1;
195} {
196  UPDATE t1 SET b='value D' WHERE a=1;
197} {
198  OMIT
199} {
200  {DELETE t1 0 X. {i 1 t {value A}} {}}
201}
202do_apply_v2_test 1.3.2 {
203  DELETE FROM t1 WHERE a=1;
204} {
205  UPDATE t1 SET b='value D' WHERE a=1;
206} {
207  REPLACE
208} {
209  {DELETE t1 1 X. {i 1 t {value A}} {}}
210}
211
212#-------------------------------------------------------------------------
213# Test cases 2.* - simple tests of rebasing actual changesets.
214#
215#    2.1.1 - 1u2u1r
216#    2.1.2 - 1u2u2r
217#    2.1.3 - 1d2d
218#    2.1.4 - 1d2u1r
219#    2.1.5 - 1d2u2r !!
220#    2.1.6 - 1u2d1r
221#    2.1.7 - 1u2d2r
222#
223#    2.1.8 - 1i2i2r
224#    2.1.9 - 1i2i1r
225#
226
227proc xConflictAbort {args} {
228  return "ABORT"
229}
230
231reset_db
232do_execsql_test 2.1.0 {
233  CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);
234  INSERT INTO t1 VALUES(1, 'one');
235  INSERT INTO t1 VALUES(2, 'two');
236  INSERT INTO t1 VALUES(3, 'three');
237}
238do_rebase_test 2.1.1 {
239  UPDATE t1 SET b = 'two.1' WHERE a=2
240} {
241  UPDATE t1 SET b = 'two.2' WHERE a=2;
242} {
243  OMIT
244} { SELECT * FROM t1 } {1 one 2 two.1 3 three}
245
246do_rebase_test 2.1.2 {
247  UPDATE t1 SET b = 'two.1' WHERE a=2
248} {
249  UPDATE t1 SET b = 'two.2' WHERE a=2;
250} {
251  REPLACE
252} { SELECT * FROM t1 } {1 one 2 two.2 3 three}
253
254do_rebase_test 2.1.3 {
255  DELETE FROM t1 WHERE a=3
256} {
257  DELETE FROM t1 WHERE a=3;
258} {
259  OMIT
260} { SELECT * FROM t1 } {1 one 2 two}
261
262do_rebase_test 2.1.4 {
263  DELETE FROM t1 WHERE a=1
264} {
265  UPDATE t1 SET b='one.2' WHERE a=1
266} {
267  OMIT
268} { SELECT * FROM t1 } {2 two 3 three}
269
270#do_rebase_test 2.1.5 {
271#  DELETE FROM t1 WHERE a=1;
272#} {
273#  UPDATE t1 SET b='one.2' WHERE a=1
274#} {
275#  REPLACE
276#} { SELECT * FROM t1 } {2 two 3 three}
277
278do_rebase_test 2.1.6 {
279  UPDATE t1 SET b='three.1' WHERE a=3
280} {
281  DELETE FROM t1 WHERE a=3;
282} {
283  OMIT
284} { SELECT * FROM t1 } {1 one 2 two 3 three.1}
285
286do_rebase_test 2.1.7 {
287  UPDATE t1 SET b='three.1' WHERE a=3
288} {
289  DELETE FROM t1 WHERE a=3;
290} {
291  REPLACE
292} { SELECT * FROM t1 } {1 one 2 two}
293
294do_rebase_test 2.1.8 {
295  INSERT INTO t1 VALUES(4, 'four.1')
296} {
297  INSERT INTO t1 VALUES(4, 'four.2');
298} {
299  REPLACE
300} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2}
301
302do_rebase_test 2.1.9 {
303  INSERT INTO t1 VALUES(4, 'four.1')
304} {
305  INSERT INTO t1 VALUES(4, 'four.2');
306} {
307  OMIT
308} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.1}
309
310do_execsql_test 2.2.0 {
311  CREATE TABLE t2(x, y, z PRIMARY KEY);
312  INSERT INTO t2 VALUES('i', 'a', 'A');
313  INSERT INTO t2 VALUES('ii', 'b', 'B');
314  INSERT INTO t2 VALUES('iii', 'c', 'C');
315
316  CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c);
317  INSERT INTO t3 VALUES(-1, 'z', 'Z');
318  INSERT INTO t3 VALUES(-2, 'y', 'Y');
319}
320
321do_rebase_test 2.2.1 {
322  UPDATE t2 SET x=1 WHERE z='A'
323} {
324  UPDATE t2 SET y='one' WHERE z='A';
325} {
326} { SELECT * FROM t2 WHERE z='A' } { 1 one A }
327
328do_rebase_test 2.2.2 {
329  UPDATE t2 SET x=1, y='one' WHERE z='B'
330} {
331  UPDATE t2 SET y='two' WHERE z='B';
332} {
333  REPLACE
334} { SELECT * FROM t2 WHERE z='B' } { 1 two B }
335
336do_rebase_test 2.2.3 {
337  UPDATE t2 SET x=1, y='one' WHERE z='B'
338} {
339  UPDATE t2 SET y='two' WHERE z='B';
340} {
341  OMIT
342} { SELECT * FROM t2 WHERE z='B' } { 1 one B }
343
344#-------------------------------------------------------------------------
345reset_db
346do_execsql_test 3.0 {
347  CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c));
348  CREATE TABLE abcdefghijkl(x PRIMARY KEY, y, z);
349
350  INSERT INTO t3 VALUES(1, 2, 3);
351  INSERT INTO t3 VALUES(4, 2, 5);
352  INSERT INTO t3 VALUES(7, 2, 9);
353
354  INSERT INTO abcdefghijkl VALUES('a', 'b', 'c');
355  INSERT INTO abcdefghijkl VALUES('d', 'e', 'f');
356  INSERT INTO abcdefghijkl VALUES('g', 'h', 'i');
357}
358
359breakpoint
360#  do_rebase_test 3.6.tn {
361#    UPDATE abcdefghijkl SET z='X', y='X' WHERE x='d';
362#  } {
363#    UPDATE abcdefghijkl SET y=1 WHERE x='d';
364#    UPDATE abcdefghijkl SET z=1 WHERE x='d';
365#  } [list REPLACE REPLACE REPLACE]
366
367foreach {tn p} {
368    1 OMIT 2 REPLACE
369} {
370  do_rebase_test 3.1.$tn {
371    INSERT INTO t3 VALUES(1, 1, 1);
372    UPDATE abcdefghijkl SET y=2;
373  } {
374    INSERT INTO t3 VALUES(4, 1, 1);
375    DELETE FROM abcdefghijkl;
376  } [list $p $p $p $p $p $p $p $p]
377
378  do_rebase_test 3.2.$tn {
379    INSERT INTO abcdefghijkl SELECT * FROM t3;
380    UPDATE t3 SET b=b+1;
381  } {
382    INSERT INTO t3 VALUES(3, 3, 3);
383    INSERT INTO abcdefghijkl SELECT * FROM t3;
384  } [list $p $p $p $p $p $p $p $p]
385
386  do_rebase_test 3.3.$tn {
387    INSERT INTO abcdefghijkl VALUES(22, 23, 24);
388  } {
389    INSERT INTO abcdefghijkl VALUES(22, 25, 26);
390    UPDATE abcdefghijkl SET y=400 WHERE x=22;
391  } [list $p $p $p $p $p $p $p $p]
392
393  do_rebase_test 3.4.$tn {
394    INSERT INTO abcdefghijkl VALUES(22, 23, 24);
395  } {
396    INSERT INTO abcdefghijkl VALUES(22, 25, 26);
397    UPDATE abcdefghijkl SET y=400 WHERE x=22;
398  } [list REPLACE $p]
399
400  do_rebase_test 3.5.$tn* {
401    UPDATE abcdefghijkl SET y='X' WHERE x='d';
402  } {
403    DELETE FROM abcdefghijkl WHERE x='d';
404    INSERT INTO abcdefghijkl VALUES('d', NULL, NULL);
405  } [list $p $p $p]
406  do_rebase_test 3.5.$tn {
407    UPDATE abcdefghijkl SET y='X' WHERE x='d';
408  } {
409    DELETE FROM abcdefghijkl WHERE x='d';
410    INSERT INTO abcdefghijkl VALUES('d', NULL, NULL);
411  } [list REPLACE $p $p]
412
413  do_rebase_test 3.6.$tn {
414    UPDATE abcdefghijkl SET z='X', y='X' WHERE x='d';
415  } {
416    UPDATE abcdefghijkl SET y=1 WHERE x='d';
417    UPDATE abcdefghijkl SET z=1 WHERE x='d';
418  } [list REPLACE $p $p]
419}
420
421#-------------------------------------------------------------------------
422# Check that apply_v2() does not create a rebase buffer for a patchset.
423# And that it is not possible to rebase a patchset.
424#
425do_execsql_test 4.0 {
426  CREATE TABLE t5(o PRIMARY KEY, p, q);
427  INSERT INTO t5 VALUES(1, 2, 3);
428  INSERT INTO t5 VALUES(4, 5, 6);
429}
430foreach {tn cmd rebasable} {
431  1 patchset 0
432  2 changeset 1
433} {
434  proc xConflict {args} { return "OMIT" }
435  do_test 4.1.$tn {
436    execsql {
437      BEGIN;
438      DELETE FROM t5 WHERE o=4;
439    }
440
441    sqlite3session S db main
442    S attach *
443    execsql {
444      INSERT INTO t5 VALUES(4, 'five', 'six');
445    }
446    set P [S $cmd]
447    S delete
448
449    execsql ROLLBACK;
450
451    set ::rebase [sqlite3changeset_apply_v2 db $P xConflict]
452    expr [llength $::rebase]>0
453  } $rebasable
454}
455
456foreach {tn cmd rebasable} {
457  1 patchset 0
458  2 changeset 1
459} {
460  do_test 4.2.$tn {
461    sqlite3session S db main
462    S attach *
463    execsql {
464      INSERT INTO t5 VALUES(5+$tn, 'five', 'six');
465    }
466    set P [S $cmd]
467    S delete
468
469    sqlite3rebaser_create R
470    R configure $::rebase
471    expr [catch {R rebase $P}]==0
472  } $rebasable
473
474  catch { R delete }
475}
476finish_test
477