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