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