Enhance the command-line completion extension to return the names of
[sqlite.git] / ext / session / session2.test
blobcd8c2869e79b507d132f63e9c06b7ed90b6c1985
1 # 2011 Mar 16
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
12 # The focus of this file is testing the session module.
15 if {![info exists testdir]} {
16   set testdir [file join [file dirname [info script]] .. .. test]
17
18 source [file join [file dirname [info script]] session_common.tcl]
19 source $testdir/tester.tcl
20 ifcapable !session {finish_test; return}
22 set testprefix session2
24 proc test_reset {} {
25   catch { db close }
26   catch { db2 close }
27   forcedelete test.db test.db2
28   sqlite3 db test.db
29   sqlite3 db2 test.db2
32 ##########################################################################
33 # End of proc definitions. Start of tests.
34 ##########################################################################
36 test_reset
37 do_execsql_test 1.0 { 
38   CREATE TABLE t1(a PRIMARY KEY, b);
39   INSERT INTO t1 VALUES('i', 'one');
41 do_iterator_test 1.1 t1 {
42   DELETE FROM t1 WHERE a = 'i';
43   INSERT INTO t1 VALUES('ii', 'two');
44 } {
45   {DELETE t1 0 X. {t i t one} {}} 
46   {INSERT t1 0 X. {} {t ii t two}}
49 do_iterator_test 1.2 t1 {
50   INSERT INTO t1 VALUES(1.5, 99.9)
51 } {
52   {INSERT t1 0 X. {} {f 1.5 f 99.9}}
55 do_iterator_test 1.3 t1 {
56   UPDATE t1 SET b = 100.1 WHERE a = 1.5;
57   UPDATE t1 SET b = 99.9 WHERE a = 1.5;
58 } { }
60 do_iterator_test 1.4 t1 {
61   UPDATE t1 SET b = 100.1 WHERE a = 1.5;
62 } {
63   {UPDATE t1 0 X. {f 1.5 f 99.9} {{} {} f 100.1}}
67 # Execute each of the following blocks of SQL on database [db1]. Collect
68 # changes using a session object. Apply the resulting changeset to
69 # database [db2]. Then check that the contents of the two databases are
70 # identical.
73 set set_of_tests {
74   1 { INSERT INTO %T1% VALUES(1, 2) } 
76   2 {
77     INSERT INTO %T2% VALUES(1, NULL);
78     INSERT INTO %T2% VALUES(2, NULL);
79     INSERT INTO %T2% VALUES(3, NULL);
80     DELETE FROM %T2% WHERE a = 2;
81     INSERT INTO %T2% VALUES(4, NULL);
82     UPDATE %T2% SET b=0 WHERE b=1;
83   } 
85   3 { INSERT INTO %T3% SELECT *, NULL FROM %T2% }
87   4 {
88     INSERT INTO %T3% SELECT a||a, b||b, NULL FROM %T3%;
89     DELETE FROM %T3% WHERE rowid%2;
90   }
92   5 { UPDATE %T3% SET c = a||b }
94   6 { UPDATE %T1% SET a = 32 }
96   7 { 
97     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
98     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
99     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
100     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
101     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
102     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
103     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
104     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
105     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
106     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
107     INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 
108     DELETE FROM %T1% WHERE (rowid%3)==0;
109   }
111   8 {
112     BEGIN;
113       INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
114     ROLLBACK;
115   }
116   9 {
117     BEGIN;
118       UPDATE %T1% SET b = 'xxx';
119     ROLLBACK;
120   }
121   10 {
122     BEGIN;
123       DELETE FROM %T1% WHERE 1;
124     ROLLBACK;
125   }
126   11 {
127     INSERT INTO %T1% VALUES(randomblob(21000), randomblob(0));
128     INSERT INTO %T1% VALUES(1.5, 1.5);
129     INSERT INTO %T1% VALUES(4.56, -99.999999999999999999999);
130   }
131   12 {
132     INSERT INTO %T2% VALUES(NULL, NULL);
133   }
135   13 {
136     DELETE FROM %T1% WHERE 1;
138     -- Insert many rows with real primary keys. Enough to force the session
139     -- objects hash table to resize. 
140     INSERT INTO %T1% VALUES(0.1, 0.1);
141     INSERT INTO %T1% SELECT a+0.1, b+0.1 FROM %T1%;
142     INSERT INTO %T1% SELECT a+0.2, b+0.2 FROM %T1%;
143     INSERT INTO %T1% SELECT a+0.4, b+0.4 FROM %T1%;
144     INSERT INTO %T1% SELECT a+0.8, b+0.8 FROM %T1%;
145     INSERT INTO %T1% SELECT a+1.6, b+1.6 FROM %T1%;
146     INSERT INTO %T1% SELECT a+3.2, b+3.2 FROM %T1%;
147     INSERT INTO %T1% SELECT a+6.4, b+6.4 FROM %T1%;
148     INSERT INTO %T1% SELECT a+12.8, b+12.8 FROM %T1%;
149     INSERT INTO %T1% SELECT a+25.6, b+25.6 FROM %T1%;
150     INSERT INTO %T1% SELECT a+51.2, b+51.2 FROM %T1%;
151     INSERT INTO %T1% SELECT a+102.4, b+102.4 FROM %T1%;
152     INSERT INTO %T1% SELECT a+204.8, b+204.8 FROM %T1%;
153   }
155   14 {
156     DELETE FROM %T1% WHERE 1;
157   }
159   15 {
160     INSERT INTO %T1% VALUES(1, 1);
161     INSERT INTO %T1% SELECT a+2, b+2 FROM %T1%;
162     INSERT INTO %T1% SELECT a+4, b+4 FROM %T1%;
163     INSERT INTO %T1% SELECT a+8, b+8 FROM %T1%;
164     INSERT INTO %T1% SELECT a+256, b+256 FROM %T1%;
165   }
167   16 {
168     INSERT INTO %T4% VALUES('abc', 'def');
169     INSERT INTO %T4% VALUES('def', 'abc');
170   }
171   17 { UPDATE %T4% SET b = 1 }
173   18 { DELETE FROM %T4% WHERE 1 }
175   19 { 
176     INSERT INTO t1 VALUES('', '');
177     INSERT INTO t1 VALUES(X'', X'');
178   }
179   20 { 
180     DELETE FROM t1;
181     INSERT INTO t1 VALUES('', NULL);
182   }
185 test_reset
186 do_common_sql {
187   CREATE TABLE t1(a PRIMARY KEY, b);
188   CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
189   CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b));
190   CREATE TABLE t4(a, b, PRIMARY KEY(b, a));
193 foreach {tn sql} [string map {%T1% t1 %T2% t2 %T3% t3 %T4% t4} $set_of_tests] {
194   do_then_apply_sql $sql
195   do_test 2.$tn { compare_db db db2 } {}
198 # The following block of tests is similar to the last, except that the
199 # session object is recording changes made to an attached database. The
200 # main database contains a table of the same name as the table being
201 # modified within the attached db.
203 test_reset
204 forcedelete test.db3
205 sqlite3 db3 test.db3
206 do_test 3.0 {
207   execsql {
208     ATTACH 'test.db3' AS 'aux';
209     CREATE TABLE t1(a, b PRIMARY KEY);
210     CREATE TABLE t2(x, y, z);
211     CREATE TABLE t3(a);
213     CREATE TABLE aux.t1(a PRIMARY KEY, b);
214     CREATE TABLE aux.t2(a, b INTEGER PRIMARY KEY);
215     CREATE TABLE aux.t3(a, b, c, PRIMARY KEY(a, b));
216     CREATE TABLE aux.t4(a, b, PRIMARY KEY(b, a));
217   }
218   execsql {
219     CREATE TABLE t1(a PRIMARY KEY, b);
220     CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
221     CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b));
222     CREATE TABLE t4(a, b, PRIMARY KEY(b, a));
223   } db2
224 } {}
226 proc xTrace {args} { puts $args }
228 foreach {tn sql} [
229   string map {%T1% aux.t1 %T2% aux.t2 %T3% aux.t3 %T4% aux.t4} $set_of_tests
230 ] {
231   do_then_apply_sql $sql aux
232   do_test 3.$tn { compare_db db2 db3 } {}
234 catch {db3 close}
237 #-------------------------------------------------------------------------
238 # The following tests verify that NULL values in primary key columns are
239 # handled correctly by the session module.
241 test_reset
242 do_execsql_test 4.0 {
243   CREATE TABLE t1(a PRIMARY KEY);
244   CREATE TABLE t2(a, b, c, PRIMARY KEY(c, b));
245   CREATE TABLE t3(a, b INTEGER PRIMARY KEY);
248 foreach {tn sql changeset} {
249   1 {
250     INSERT INTO t1 VALUES(123);
251     INSERT INTO t1 VALUES(NULL);
252     INSERT INTO t1 VALUES(456);
253   } {
254     {INSERT t1 0 X {} {i 456}} 
255     {INSERT t1 0 X {} {i 123}}
256   }
258   2 {
259     UPDATE t1 SET a = NULL;
260   } {
261     {DELETE t1 0 X {i 456} {}}
262     {DELETE t1 0 X {i 123} {}}
263   }
265   3 { DELETE FROM t1 } { }
267   4 { 
268     INSERT INTO t3 VALUES(NULL, NULL)
269   } {
270     {INSERT t3 0 .X {} {n {} i 1}} 
271   }
273   5 { INSERT INTO t2 VALUES(1, 2, NULL) }    { }
274   6 { INSERT INTO t2 VALUES(1, NULL, 3) }    { }
275   7 { INSERT INTO t2 VALUES(1, NULL, NULL) } { }
276   8 { INSERT INTO t2 VALUES(1, 2, 3) }    { {INSERT t2 0 .XX {} {i 1 i 2 i 3}} }
277   9 { DELETE FROM t2 WHERE 1 }            { {DELETE t2 0 .XX {i 1 i 2 i 3} {}} }
279 } {
280   do_iterator_test 4.$tn {t1 t2 t3} $sql $changeset
284 #-------------------------------------------------------------------------
285 # Test that if NULL is passed to sqlite3session_attach(), all database
286 # tables are attached to the session object.
288 test_reset
289 do_execsql_test 5.0 {
290   CREATE TABLE t1(a PRIMARY KEY);
291   CREATE TABLE t2(x, y PRIMARY KEY);
294 foreach {tn sql changeset} {
295   1 { INSERT INTO t1 VALUES(35) }     { {INSERT t1 0 X {} {i 35}} }
296   2 { INSERT INTO t2 VALUES(36, 37) } { {INSERT t2 0 .X {} {i 36 i 37}} }
297   3 { 
298     DELETE FROM t1 WHERE 1;
299     UPDATE t2 SET x = 34;
300   } { 
301     {DELETE t1 0 X {i 35} {}}
302     {UPDATE t2 0 .X {i 36 i 37} {i 34 {} {}}}
303   }
304 } {
305   do_iterator_test 5.$tn * $sql $changeset
308 #-------------------------------------------------------------------------
309 # The next block of tests verify that the "indirect" flag is set 
310 # correctly within changesets. The indirect flag is set for a change
311 # if either of the following are true:
313 #   * The sqlite3session_indirect() API has been used to set the session
314 #     indirect flag to true, or
315 #   * The change was made by a trigger.
317 # If the same row is updated more than once during a session, then the 
318 # change is considered indirect only if all changes meet the criteria 
319 # above.
321 test_reset
322 db function indirect [list S indirect]
324 do_execsql_test 6.0 {
325   CREATE TABLE t1(a PRIMARY KEY, b, c);
327   CREATE TABLE t2(x PRIMARY KEY, y);
328   CREATE TRIGGER AFTER INSERT ON t2 WHEN new.x%2 BEGIN
329     INSERT INTO t2 VALUES(new.x+1, NULL);
330   END;
333 do_iterator_test 6.1.1 * {
334   INSERT INTO t1 VALUES(1, 'one', 'i');
335   SELECT indirect(1);
336   INSERT INTO t1 VALUES(2, 'two', 'ii');
337   SELECT indirect(0);
338   INSERT INTO t1 VALUES(3, 'three', 'iii');
339 } {
340   {INSERT t1 0 X.. {} {i 1 t one t i}}
341   {INSERT t1 1 X.. {} {i 2 t two t ii}}
342   {INSERT t1 0 X.. {} {i 3 t three t iii}}
345 do_iterator_test 6.1.2 * {
346   SELECT indirect(1);
347   UPDATE t1 SET c = 'I' WHERE a = 1;
348   SELECT indirect(0);
349 } {
350   {UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}}
352 do_iterator_test 6.1.3 * {
353   SELECT indirect(1);
354   UPDATE t1 SET c = '.' WHERE a = 1;
355   SELECT indirect(0);
356   UPDATE t1 SET c = 'o' WHERE a = 1;
357 } {
358   {UPDATE t1 0 X.. {i 1 {} {} t I} {{} {} {} {} t o}}
360 do_iterator_test 6.1.4 * {
361   SELECT indirect(0);
362   UPDATE t1 SET c = 'x' WHERE a = 1;
363   SELECT indirect(1);
364   UPDATE t1 SET c = 'i' WHERE a = 1;
365 } {
366   {UPDATE t1 0 X.. {i 1 {} {} t o} {{} {} {} {} t i}}
368 do_iterator_test 6.1.4 * {
369   SELECT indirect(1);
370   UPDATE t1 SET c = 'y' WHERE a = 1;
371   SELECT indirect(1);
372   UPDATE t1 SET c = 'I' WHERE a = 1;
373 } {
374   {UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}}
377 do_iterator_test 6.1.5 * {
378   INSERT INTO t2 VALUES(1, 'x');
379 } {
380   {INSERT t2 0 X. {} {i 1 t x}}
381   {INSERT t2 1 X. {} {i 2 n {}}}
384 do_iterator_test 6.1.6 * {
385   SELECT indirect(1);
386   INSERT INTO t2 VALUES(3, 'x');
387   SELECT indirect(0);
388   UPDATE t2 SET y = 'y' WHERE x>2;
389 } {
390   {INSERT t2 0 X. {} {i 3 t y}}
391   {INSERT t2 0 X. {} {i 4 t y}}
394 do_iterator_test 6.1.7 * {
395   SELECT indirect(1);
396   DELETE FROM t2 WHERE x = 4;
397   SELECT indirect(0);
398   INSERT INTO t2 VALUES(4, 'new');
399 } {
400   {UPDATE t2 0 X. {i 4 t y} {{} {} t new}}
403 do_iterator_test 6.1.8 * {
404   CREATE TABLE t3(a, b PRIMARY KEY);
405   CREATE TABLE t4(a, b PRIMARY KEY);
406   CREATE TRIGGER t4t AFTER UPDATE ON t4 BEGIN
407     UPDATE t3 SET a = new.a WHERE b = new.b;
408   END;
410   SELECT indirect(1);
411   INSERT INTO t3 VALUES('one', 1);
412   INSERT INTO t4 VALUES('one', 1);
413   SELECT indirect(0);
414   UPDATE t4 SET a = 'two' WHERE b = 1;
415 } {
416   {INSERT t3 1 .X {} {t two i 1}}
417   {INSERT t4 0 .X {} {t two i 1}} 
420 sqlite3session S db main
421 do_execsql_test 6.2.1 {
422   SELECT indirect(0);
423   SELECT indirect(-1);
424   SELECT indirect(45);
425   SELECT indirect(-100);
426 } {0 0 1 1}
427 S delete
429 #-------------------------------------------------------------------------
430 # Test that if a conflict-handler that has been passed either NOTFOUND or
431 # CONSTRAINT returns REPLACE - the sqlite3changeset_apply() call returns
432 # MISUSE and rolls back any changes made so far.
434 #   7.1.*: NOTFOUND conflict-callback.
435 #   7.2.*: CONSTRAINT conflict-callback.
437 proc xConflict {args} {return REPLACE}
438 test_reset
440 do_execsql_test 7.1.1 {
441   CREATE TABLE t1(a PRIMARY KEY, b);
442   INSERT INTO t1 VALUES(1, 'one');
443   INSERT INTO t1 VALUES(2, 'two');
445 do_test 7.1.2 {
446   execsql {
447     CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
448     INSERT INTO t1 VALUES(1, 'one');
449   } db2
450 } {}
451 do_test 7.1.3 {
452   set changeset [changeset_from_sql {
453     UPDATE t1 SET b = 'five' WHERE a = 1;
454     UPDATE t1 SET b = 'six' WHERE a = 2;
455   }]
456   set x [list]
457   sqlite3session_foreach c $changeset { lappend x $c }
458   set x
459 } [list \
460   {UPDATE t1 0 X. {i 1 t one} {{} {} t five}} \
461   {UPDATE t1 0 X. {i 2 t two} {{} {} t six}}  \
463 do_test 7.1.4 {
464   list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
465 } {1 SQLITE_MISUSE}
466 do_test 7.1.5 { execsql { SELECT * FROM t1 } db2 } {1 one}
468 do_test 7.2.1 {
469   set changeset [changeset_from_sql { UPDATE t1 SET b = NULL WHERE a = 1 }]
471   set x [list]
472   sqlite3session_foreach c $changeset { lappend x $c }
473   set x
474 } [list \
475   {UPDATE t1 0 X. {i 1 t five} {{} {} n {}}} \
477 do_test 7.2.2 {
478   list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
479 } {1 SQLITE_MISUSE}
480 do_test 7.2.3 { execsql { SELECT * FROM t1 } db2 } {1 one}
482 #-------------------------------------------------------------------------
483 # Test that if a conflict-handler returns ABORT, application of the 
484 # changeset is rolled back and the sqlite3changeset_apply() method returns
485 # SQLITE_ABORT.
487 # Also test that the same thing happens if a conflict handler returns an
488 # unrecognized integer value. Except, in this case SQLITE_MISUSE is returned
489 # instead of SQLITE_ABORT.
491 foreach {tn conflict_return apply_return} {
492   1    ABORT   SQLITE_ABORT
493   2    567     SQLITE_MISUSE
494 } {
495   test_reset
496   proc xConflict {args} [list return $conflict_return]
498   do_test 8.$tn.0 {
499     do_common_sql { 
500       CREATE TABLE t1(x, y, PRIMARY KEY(x, y));
501       INSERT INTO t1 VALUES('x', 'y');
502     }
503     execsql { INSERT INTO t1 VALUES('w', 'w') }
505     set changeset [changeset_from_sql { DELETE FROM t1 WHERE 1 }]
507     set x [list]
508     sqlite3session_foreach c $changeset { lappend x $c }
509     set x
510   } [list \
511     {DELETE t1 0 XX {t w t w} {}} \
512     {DELETE t1 0 XX {t x t y} {}} \
513   ]
515   do_test 8.$tn.1 {
516     list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
517   } [list 1 $apply_return]
519   do_test 8.$tn.2 {
520     execsql {SELECT * FROM t1} db2
521   } {x y}
525 #-------------------------------------------------------------------------
526 # Try to cause an infinite loop as follows:
528 #   1. Have a changeset insert a row that causes a CONFLICT callback,
529 #   2. Have the conflict handler return REPLACE,
530 #   3. After the session module deletes the conflicting row, have a trigger
531 #      re-insert it.
532 #   4. Goto step 1...
534 # This doesn't work, as the second invocation of the conflict handler is a
535 # CONSTRAINT, not a CONFLICT. There is at most one CONFLICT callback for
536 # each change in the changeset.
538 test_reset
539 proc xConflict {type args} { 
540   if {$type == "CONFLICT"} { return REPLACE }
541   return OMIT
543 do_test 9.1 {
544   execsql {
545     CREATE TABLE t1(a PRIMARY KEY, b);
546   }
547   execsql {
548     CREATE TABLE t1(a PRIMARY KEY, b);
549     INSERT INTO t1 VALUES('x', 2);
550     CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
551       INSERT INTO t1 VALUES(old.a, old.b);
552     END;
553   } db2
554 } {}
555 do_test 9.2 {
556   set changeset [changeset_from_sql { INSERT INTO t1 VALUES('x', 1) }]
557   sqlite3changeset_apply db2 $changeset xConflict
558 } {}
559 do_test 9.3 {
560   execsql { SELECT * FROM t1 } db2
561 } {x 2}
563 #-------------------------------------------------------------------------
565 test_reset
566 db function enable [list S enable]
568 do_common_sql {
569   CREATE TABLE t1(a PRIMARY KEY, b);
570   INSERT INTO t1 VALUES('x', 'X');
573 do_iterator_test 10.1 t1 {
574   INSERT INTO t1 VALUES('y', 'Y');
575   SELECT enable(0);
576   INSERT INTO t1 VALUES('z', 'Z');
577   SELECT enable(1);
578 } {
579   {INSERT t1 0 X. {} {t y t Y}}
582 sqlite3session S db main
583 do_execsql_test 10.2 {
584   SELECT enable(0);
585   SELECT enable(-1);
586   SELECT enable(1);
587   SELECT enable(-1);
588 } {0 0 1 1}
589 S delete
591 finish_test