xref: /sqlite-3.40.0/ext/session/session1.test (revision db04571c)
1# 2011 March 07
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
19
20set testprefix session1
21
22proc do_changeset_test {tn session res} {
23  set r [list]
24  foreach x $res {lappend r $x}
25  uplevel do_test $tn [list [subst -nocommands {
26    set x [list]
27    sqlite3session_foreach c [$session changeset] { lappend x [set c] }
28    set x
29  }]] [list $r]
30}
31
32proc do_changeset_invert_test {tn session res} {
33  set r [list]
34  foreach x $res {lappend r $x}
35  uplevel do_test $tn [list [subst -nocommands {
36    set x [list]
37    set changeset [sqlite3changeset_invert [$session changeset]]
38    sqlite3session_foreach c [set changeset] { lappend x [set c] }
39    set x
40  }]] [list $r]
41}
42
43do_execsql_test 1.0 {
44  CREATE TABLE t1(x PRIMARY KEY, y);
45  INSERT INTO t1 VALUES('abc', 'def');
46}
47
48#-------------------------------------------------------------------------
49# Test creating, attaching tables to and deleting session objects.
50#
51do_test 1.1 { sqlite3session S db main } {S}
52do_test 1.2 { S delete } {}
53do_test 1.3 { sqlite3session S db main } {S}
54do_test 1.4 { S attach t1 } {}
55do_test 1.5 { S delete } {}
56do_test 1.6 { sqlite3session S db main } {S}
57do_test 1.7 { S attach t1 ; S attach t2 ; S attach t3 } {}
58do_test 1.8 { S attach t1 ; S attach t2 ; S attach t3 } {}
59do_test 1.9 { S delete } {}
60do_test 1.10 {
61  sqlite3session S db main
62  S attach t1
63  execsql { INSERT INTO t1 VALUES('ghi', 'jkl') }
64} {}
65do_test 1.11 { S delete } {}
66do_test 1.12 {
67  sqlite3session S db main
68  S attach t1
69  execsql { INSERT INTO t1 VALUES('mno', 'pqr') }
70  execsql { UPDATE t1 SET x = 111 WHERE rowid = 1 }
71  execsql { DELETE FROM t1 WHERE rowid = 2 }
72} {}
73do_test 1.13 {
74  S changeset
75  S delete
76} {}
77
78#-------------------------------------------------------------------------
79# Simple changeset tests. Also test the sqlite3changeset_invert()
80# function.
81#
82do_test 2.1.1 {
83  execsql { DELETE FROM t1 }
84  sqlite3session S db main
85  S attach t1
86  execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
87  execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
88  execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
89} {}
90do_changeset_test 2.1.2 S {
91  {INSERT t1 {} {i 1 t Sukhothai}}
92  {INSERT t1 {} {i 2 t Ayutthaya}}
93  {INSERT t1 {} {i 3 t Thonburi}}
94}
95do_changeset_invert_test 2.1.3 S {
96  {DELETE t1 {i 1 t Sukhothai} {}}
97  {DELETE t1 {i 2 t Ayutthaya} {}}
98  {DELETE t1 {i 3 t Thonburi} {}}
99}
100do_test 2.1.4 { S delete } {}
101
102do_test 2.2.1 {
103  sqlite3session S db main
104  S attach t1
105  execsql { DELETE FROM t1 WHERE 1 }
106} {}
107do_changeset_test 2.2.2 S {
108  {DELETE t1 {i 1 t Sukhothai} {}}
109  {DELETE t1 {i 2 t Ayutthaya} {}}
110  {DELETE t1 {i 3 t Thonburi} {}}
111}
112do_changeset_invert_test 2.2.3 S {
113  {INSERT t1 {} {i 1 t Sukhothai}}
114  {INSERT t1 {} {i 2 t Ayutthaya}}
115  {INSERT t1 {} {i 3 t Thonburi}}
116}
117do_test 2.2.4 { S delete } {}
118
119do_test 2.3.1 {
120  execsql { DELETE FROM t1 }
121  sqlite3session S db main
122  execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
123  execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
124  execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
125  S attach t1
126  execsql {
127    UPDATE t1 SET x = 10 WHERE x = 1;
128    UPDATE t1 SET y = 'Surin' WHERE x = 2;
129    UPDATE t1 SET x = 20, y = 'Thapae' WHERE x = 3;
130  }
131} {}
132
133do_changeset_test 2.3.2 S {
134  {INSERT t1 {} {i 10 t Sukhothai}}
135  {DELETE t1 {i 1 t Sukhothai} {}}
136  {UPDATE t1 {i 2 t Ayutthaya} {{} {} t Surin}}
137  {DELETE t1 {i 3 t Thonburi} {}}
138  {INSERT t1 {} {i 20 t Thapae}}
139}
140
141do_changeset_invert_test 2.3.3 S {
142  {DELETE t1 {i 10 t Sukhothai} {}}
143  {INSERT t1 {} {i 1 t Sukhothai}}
144  {UPDATE t1 {{} {} t Surin} {i 2 t Ayutthaya}}
145  {INSERT t1 {} {i 3 t Thonburi}}
146  {DELETE t1 {i 20 t Thapae} {}}
147}
148do_test 2.3.4 { S delete } {}
149
150do_test 2.4.1 {
151  sqlite3session S db main
152  S attach t1
153  execsql { INSERT INTO t1 VALUES(100, 'Bangkok') }
154  execsql { DELETE FROM t1 WHERE x = 100 }
155} {}
156do_changeset_test 2.4.2 S {}
157do_changeset_invert_test 2.4.3 S {}
158do_test 2.4.4 { S delete } {}
159
160#-------------------------------------------------------------------------
161# Test the application of simple changesets. These tests also test that
162# the conflict callback is invoked correctly. For these tests, the
163# conflict callback always returns OMIT.
164#
165db close
166forcedelete test.db test.db2
167sqlite3 db test.db
168sqlite3 db2 test.db2
169
170proc xConflict {args} {
171  lappend ::xConflict $args
172  return ""
173}
174
175proc bgerror {args} { set ::background_error $args }
176
177proc do_conflict_test {tn args} {
178  set O(-tables)    [list]
179  set O(-sql)       [list]
180  set O(-conflicts) [list]
181
182  array set V $args
183  foreach key [array names V] {
184    if {![info exists O($key)]} {error "no such option: $key"}
185  }
186  array set O $args
187
188  sqlite3session S db main
189  foreach t $O(-tables) { S attach $t }
190  execsql $O(-sql)
191  set ::xConflict [list]
192  sqlite3changeset_apply db2 [S changeset] xConflict
193
194  set conflicts [list]
195  foreach c $O(-conflicts) {
196    lappend conflicts $c
197  }
198
199  after 1 {set go 1}
200  vwait go
201
202  uplevel do_test $tn [list { set ::xConflict }] [list $conflicts]
203  S delete
204}
205
206proc do_db2_test {testname sql {result {}}} {
207  uplevel do_test $testname [list "execsql {$sql} db2"] [list [list {*}$result]]
208}
209
210# Test INSERT changesets.
211#
212do_test 3.1.0 {
213  execsql { CREATE TABLE t1(a PRIMARY KEY, b NOT NULL) } db2
214  execsql {
215    CREATE TABLE t1(a PRIMARY KEY, b);
216    INSERT INTO t1 VALUES(1, 'one');
217    INSERT INTO t1 VALUES(2, 'two');
218  } db
219} {}
220do_db2_test 3.1.1 "INSERT INTO t1 VALUES(6, 'VI')"
221do_conflict_test 3.1.2 -tables t1 -sql {
222  INSERT INTO t1 VALUES(3, 'three');
223  INSERT INTO t1 VALUES(4, 'four');
224  INSERT INTO t1 VALUES(5, 'five');
225  INSERT INTO t1 VALUES(6, 'six');
226  INSERT INTO t1 VALUES(7, 'seven');
227  INSERT INTO t1 VALUES(8, NULL);
228} -conflicts {
229  {INSERT t1 CONSTRAINT {i 8 n {}}}
230  {INSERT t1 CONFLICT {i 6 t six} {i 6 t VI}}
231}
232
233do_db2_test 3.1.3 "SELECT * FROM t1" {
234  6 VI 3 three 4 four 5 five 7 seven
235}
236do_execsql_test 3.1.4 "SELECT * FROM t1" {
237  1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 {}
238}
239
240# Test DELETE changesets.
241#
242do_execsql_test 3.2.1 {
243  PRAGMA foreign_keys = on;
244  CREATE TABLE t2(a PRIMARY KEY, b);
245  CREATE TABLE t3(c, d REFERENCES t2);
246  INSERT INTO t2 VALUES(1, 'one');
247  INSERT INTO t2 VALUES(2, 'two');
248  INSERT INTO t2 VALUES(3, 'three');
249  INSERT INTO t2 VALUES(4, 'four');
250}
251do_db2_test 3.2.2 {
252  PRAGMA foreign_keys = on;
253  CREATE TABLE t2(a PRIMARY KEY, b);
254  CREATE TABLE t3(c, d REFERENCES t2);
255  INSERT INTO t2 VALUES(1, 'one');
256  INSERT INTO t2 VALUES(2, 'two');
257  INSERT INTO t2 VALUES(4, 'five');
258  INSERT INTO t3 VALUES('i', 1);
259}
260do_conflict_test 3.2.3 -tables t2 -sql {
261  DELETE FROM t2 WHERE a = 1;
262  DELETE FROM t2 WHERE a = 2;
263  DELETE FROM t2 WHERE a = 3;
264  DELETE FROM t2 WHERE a = 4;
265} -conflicts {
266  {DELETE t2 CONSTRAINT {i 1 t one}}
267  {DELETE t2 NOTFOUND {i 3 t three}}
268  {DELETE t2 DATA {i 4 t four} {i 4 t five}}
269}
270do_execsql_test 3.2.4 "SELECT * FROM t2" {}
271do_db2_test     3.2.5 "SELECT * FROM t2" {1 one 4 five}
272
273# Test UPDATE changesets.
274#
275do_execsql_test 3.3.1 {
276  CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c));
277  INSERT INTO t4 VALUES(1, 2, 3);
278  INSERT INTO t4 VALUES(4, 5, 6);
279  INSERT INTO t4 VALUES(7, 8, 9);
280  INSERT INTO t4 VALUES(10, 11, 12);
281}
282do_db2_test 3.3.2 {
283  CREATE TABLE t4(a NOT NULL, b, c, PRIMARY KEY(b, c));
284  INSERT INTO t4 VALUES(0, 2, 3);
285  INSERT INTO t4 VALUES(4, 5, 7);
286  INSERT INTO t4 VALUES(7, 8, 9);
287  INSERT INTO t4 VALUES(10, 11, 12);
288}
289do_conflict_test 3.3.3 -tables t4 -sql {
290  UPDATE t4 SET a = -1 WHERE b = 2;
291  UPDATE t4 SET a = -1 WHERE b = 5;
292  UPDATE t4 SET a = NULL WHERE c = 9;
293  UPDATE t4 SET a = 'x' WHERE b = 11;
294} -conflicts {
295  {UPDATE t4 CONSTRAINT {i 7 i 8 i 9} {n {} {} {} {} {}}}
296  {UPDATE t4 DATA {i 1 i 2 i 3} {i -1 {} {} {} {}} {i 0 i 2 i 3}}
297  {UPDATE t4 NOTFOUND {i 4 i 5 i 6} {i -1 {} {} {} {}}}
298}
299do_db2_test     3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12}
300do_execsql_test 3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12}
301
302#-------------------------------------------------------------------------
303# This next block of tests verifies that values returned by the conflict
304# handler are intepreted correctly.
305#
306
307proc test_reset {} {
308  db close
309  db2 close
310  forcedelete test.db test.db2
311  sqlite3 db test.db
312  sqlite3 db2 test.db2
313}
314
315proc xConflict {args} {
316  lappend ::xConflict $args
317  return $::conflict_return
318}
319
320foreach {tn conflict_return after} {
321  1 OMIT      {1 2 value1   4 5 7       10 x x}
322  2 REPLACE   {1 2 value1   4 5 value2  10 8 9}
323} {
324  test_reset
325
326  do_test 4.$tn.1 {
327    foreach db {db db2} {
328      execsql {
329        CREATE TABLE t1(a, b, c, PRIMARY KEY(a));
330        INSERT INTO t1 VALUES(1, 2, 3);
331        INSERT INTO t1 VALUES(4, 5, 6);
332        INSERT INTO t1 VALUES(7, 8, 9);
333      } $db
334    }
335    execsql {
336      REPLACE INTO t1 VALUES(4, 5, 7);
337      REPLACE INTO t1 VALUES(10, 'x', 'x');
338    } db2
339  } {}
340
341  do_conflict_test 4.$tn.2 -tables t1 -sql {
342    UPDATE t1 SET c = 'value1' WHERE a = 1;       -- no conflict
343    UPDATE t1 SET c = 'value2' WHERE a = 4;       -- DATA conflict
344    UPDATE t1 SET a = 10 WHERE a = 7;             -- CONFLICT conflict
345  } -conflicts {
346    {INSERT t1 CONFLICT {i 10 i 8 i 9} {i 10 t x t x}}
347    {UPDATE t1 DATA {i 4 {} {} i 6} {{} {} {} {} t value2} {i 4 i 5 i 7}}
348  }
349
350  do_db2_test 4.$tn.3 "SELECT * FROM t1 ORDER BY a" $after
351}
352
353foreach {tn conflict_return} {
354  1 OMIT
355  2 REPLACE
356} {
357  test_reset
358
359  do_test 5.$tn.1 {
360    # Create an identical schema in both databases.
361    set schema {
362      CREATE TABLE "'foolish name'"(x, y, z, PRIMARY KEY(x, y));
363    }
364    execsql $schema db
365    execsql $schema db2
366
367    # Add some rows to [db2]. These rows will cause conflicts later
368    # on when the changeset from [db] is applied to it.
369    execsql {
370      INSERT INTO "'foolish name'" VALUES('one', 'one', 'ii');
371      INSERT INTO "'foolish name'" VALUES('one', 'two', 'i');
372      INSERT INTO "'foolish name'" VALUES('two', 'two', 'ii');
373    } db2
374
375  } {}
376
377  do_conflict_test 5.$tn.2 -tables {{'foolish name'}} -sql {
378    INSERT INTO "'foolish name'" VALUES('one', 'two', 2);
379  } -conflicts {
380    {INSERT {'foolish name'} CONFLICT {t one t two i 2} {t one t two t i}}
381  }
382
383  set res(REPLACE) {one one ii one two 2 two two ii}
384  set res(OMIT)    {one one ii one two i two two ii}
385  do_db2_test 5.$tn.3 {
386    SELECT * FROM "'foolish name'" ORDER BY x, y
387  } $res($conflict_return)
388
389
390  do_test 5.$tn.1 {
391    set schema {
392      CREATE TABLE d1("z""z" PRIMARY KEY, y);
393      INSERT INTO d1 VALUES(1, 'one');
394      INSERT INTO d1 VALUES(2, 'two');
395    }
396    execsql $schema db
397    execsql $schema db2
398
399    execsql {
400      UPDATE d1 SET y = 'TWO' WHERE "z""z" = 2;
401    } db2
402
403  } {}
404
405  do_conflict_test 5.$tn.2 -tables d1 -sql {
406    DELETE FROM d1 WHERE "z""z" = 2;
407  } -conflicts {
408    {DELETE d1 DATA {i 2 t two} {i 2 t TWO}}
409  }
410
411  set res(REPLACE) {1 one}
412  set res(OMIT)    {1 one 2 TWO}
413  do_db2_test 5.$tn.3 "SELECT * FROM d1" $res($conflict_return)
414}
415
416#-------------------------------------------------------------------------
417# Test that two tables can be monitored by a single session object.
418#
419test_reset
420set schema {
421  CREATE TABLE t1(a COLLATE nocase PRIMARY KEY, b);
422  CREATE TABLE t2(a, b PRIMARY KEY);
423}
424do_test 6.0 {
425  execsql $schema db
426  execsql $schema db2
427  execsql {
428    INSERT INTO t1 VALUES('a', 'b');
429    INSERT INTO t2 VALUES('a', 'b');
430  } db2
431} {}
432
433set conflict_return ""
434do_conflict_test 6.1 -tables {t1 t2} -sql {
435  INSERT INTO t1 VALUES('1', '2');
436  INSERT INTO t1 VALUES('A', 'B');
437  INSERT INTO t2 VALUES('A', 'B');
438} -conflicts {
439  {INSERT t1 CONFLICT {t A t B} {t a t b}}
440}
441
442do_db2_test 6.2 "SELECT * FROM t1" {a b 1 2}
443do_db2_test 6.3 "SELECT * FROM t2" {a b A B}
444
445catch { db2 close }
446finish_test
447
448