| OLD | NEW | 
 | (Empty) | 
|    1 # 2009 August 24 |  | 
|    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 # |  | 
|   12  |  | 
|   13 set testdir [file dirname $argv0] |  | 
|   14 source $testdir/tester.tcl |  | 
|   15 ifcapable {!trigger} { |  | 
|   16   finish_test |  | 
|   17   return |  | 
|   18 } |  | 
|   19  |  | 
|   20 #------------------------------------------------------------------------- |  | 
|   21 # Test organization: |  | 
|   22 # |  | 
|   23 # triggerC-1.*: Haphazardly designed trigger related tests that were useful |  | 
|   24 #               during an upgrade of the triggers sub-system. |  | 
|   25 #  |  | 
|   26 # triggerC-2.*: |  | 
|   27 # |  | 
|   28 # triggerC-3.*: |  | 
|   29 # |  | 
|   30 # triggerC-4.*: |  | 
|   31 # |  | 
|   32 # triggerC-5.*: Test that when recursive triggers are enabled DELETE  |  | 
|   33 #               triggers are fired when rows are deleted as part of OR |  | 
|   34 #               REPLACE conflict resolution. And that they are not fired |  | 
|   35 #               if recursive triggers are not enabled. |  | 
|   36 # |  | 
|   37 # triggerC-6.*: Test that the recursive_triggers pragma returns correct |  | 
|   38 #               results when invoked without an argument. |  | 
|   39 # |  | 
|   40  |  | 
|   41 # Enable recursive triggers for this file. |  | 
|   42 # |  | 
|   43 execsql { PRAGMA recursive_triggers = on } |  | 
|   44  |  | 
|   45 #sqlite3_db_config_lookaside db 0 0 0 |  | 
|   46  |  | 
|   47 #------------------------------------------------------------------------- |  | 
|   48 # This block of tests, triggerC-1.*, are not aimed at any specific |  | 
|   49 # property of the triggers sub-system. They were created to debug |  | 
|   50 # specific problems while modifying SQLite to support recursive |  | 
|   51 # triggers. They are left here in case they can help debug the |  | 
|   52 # same problems again. |  | 
|   53 # |  | 
|   54 do_test triggerC-1.1 { |  | 
|   55   execsql { |  | 
|   56     CREATE TABLE t1(a, b, c); |  | 
|   57     CREATE TABLE log(t, a1, b1, c1, a2, b2, c2); |  | 
|   58     CREATE TRIGGER trig1 BEFORE INSERT ON t1 BEGIN |  | 
|   59       INSERT INTO log VALUES('before', NULL, NULL, NULL, new.a, new.b, new.c); |  | 
|   60     END; |  | 
|   61     CREATE TRIGGER trig2 AFTER INSERT ON t1 BEGIN |  | 
|   62       INSERT INTO log VALUES('after', NULL, NULL, NULL, new.a, new.b, new.c); |  | 
|   63     END; |  | 
|   64     CREATE TRIGGER trig3 BEFORE UPDATE ON t1 BEGIN |  | 
|   65       INSERT INTO log VALUES('before', old.a,old.b,old.c, new.a,new.b,new.c); |  | 
|   66     END; |  | 
|   67     CREATE TRIGGER trig4 AFTER UPDATE ON t1 BEGIN |  | 
|   68       INSERT INTO log VALUES('after', old.a,old.b,old.c, new.a,new.b,new.c); |  | 
|   69     END; |  | 
|   70  |  | 
|   71     CREATE TRIGGER trig5 BEFORE DELETE ON t1 BEGIN |  | 
|   72       INSERT INTO log VALUES('before', old.a,old.b,old.c, NULL,NULL,NULL); |  | 
|   73     END; |  | 
|   74     CREATE TRIGGER trig6 AFTER DELETE ON t1 BEGIN |  | 
|   75       INSERT INTO log VALUES('after', old.a,old.b,old.c, NULL,NULL,NULL); |  | 
|   76     END; |  | 
|   77   } |  | 
|   78 } {} |  | 
|   79 do_test triggerC-1.2 { |  | 
|   80   execsql { |  | 
|   81     INSERT INTO t1 VALUES('A', 'B', 'C'); |  | 
|   82     SELECT * FROM log; |  | 
|   83   } |  | 
|   84 } {before {} {} {} A B C after {} {} {} A B C} |  | 
|   85 do_test triggerC-1.3 { |  | 
|   86   execsql { SELECT * FROM t1 } |  | 
|   87 } {A B C} |  | 
|   88 do_test triggerC-1.4 { |  | 
|   89   execsql { |  | 
|   90     DELETE FROM log; |  | 
|   91     UPDATE t1 SET a = 'a'; |  | 
|   92     SELECT * FROM log; |  | 
|   93   } |  | 
|   94 } {before A B C a B C after A B C a B C} |  | 
|   95 do_test triggerC-1.5 { |  | 
|   96   execsql { SELECT * FROM t1 } |  | 
|   97 } {a B C} |  | 
|   98 do_test triggerC-1.6 { |  | 
|   99   execsql { |  | 
|  100     DELETE FROM log; |  | 
|  101     DELETE FROM t1; |  | 
|  102     SELECT * FROM log; |  | 
|  103   } |  | 
|  104 } {before a B C {} {} {} after a B C {} {} {}} |  | 
|  105 do_test triggerC-1.7 { |  | 
|  106   execsql { SELECT * FROM t1 } |  | 
|  107 } {} |  | 
|  108 do_test triggerC-1.8 { |  | 
|  109   execsql { |  | 
|  110     CREATE TABLE t4(a, b); |  | 
|  111     CREATE TRIGGER t4t AFTER DELETE ON t4 BEGIN |  | 
|  112       SELECT RAISE(ABORT, 'delete is not supported'); |  | 
|  113     END; |  | 
|  114   } |  | 
|  115 } {} |  | 
|  116 do_test triggerC-1.9 { |  | 
|  117   execsql { INSERT INTO t4 VALUES(1, 2) } |  | 
|  118   catchsql { DELETE FROM t4 } |  | 
|  119 } {1 {delete is not supported}} |  | 
|  120 do_test triggerC-1.10 { |  | 
|  121   execsql { SELECT * FROM t4 } |  | 
|  122 } {1 2} |  | 
|  123 do_test triggerC-1.11 { |  | 
|  124   execsql { |  | 
|  125     CREATE TABLE t5 (a primary key, b, c); |  | 
|  126     INSERT INTO t5 values (1, 2, 3); |  | 
|  127     CREATE TRIGGER au_tbl AFTER UPDATE ON t5 BEGIN |  | 
|  128       UPDATE OR IGNORE t5 SET a = new.a, c = 10; |  | 
|  129     END; |  | 
|  130   } |  | 
|  131 } {} |  | 
|  132 do_test triggerC-1.12 { |  | 
|  133   catchsql { UPDATE OR REPLACE t5 SET a = 4 WHERE a = 1 } |  | 
|  134 } {1 {too many levels of trigger recursion}} |  | 
|  135 do_test triggerC-1.13 { |  | 
|  136   execsql { |  | 
|  137     CREATE TABLE t6(a INTEGER PRIMARY KEY, b); |  | 
|  138     INSERT INTO t6 VALUES(1, 2); |  | 
|  139     create trigger r1 after update on t6 for each row begin |  | 
|  140       SELECT 1; |  | 
|  141     end; |  | 
|  142     UPDATE t6 SET a=a;  |  | 
|  143   } |  | 
|  144 } {} |  | 
|  145 do_test triggerC-1.14 { |  | 
|  146   execsql { |  | 
|  147     DROP TABLE t1; |  | 
|  148     CREATE TABLE cnt(n); |  | 
|  149     INSERT INTO cnt VALUES(0); |  | 
|  150     CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE, c, d, e); |  | 
|  151     CREATE INDEX t1cd ON t1(c,d); |  | 
|  152     CREATE TRIGGER t1r1 AFTER UPDATE ON t1 BEGIN UPDATE cnt SET n=n+1; END; |  | 
|  153     INSERT INTO t1 VALUES(1,2,3,4,5); |  | 
|  154     INSERT INTO t1 VALUES(6,7,8,9,10); |  | 
|  155     INSERT INTO t1 VALUES(11,12,13,14,15); |  | 
|  156   } |  | 
|  157 } {} |  | 
|  158 do_test triggerC-1.15 { |  | 
|  159   catchsql { UPDATE OR ROLLBACK t1 SET a=100 } |  | 
|  160 } {1 {PRIMARY KEY must be unique}} |  | 
|  161  |  | 
|  162  |  | 
|  163 #------------------------------------------------------------------------- |  | 
|  164 # This block of tests, triggerC-2.*, tests that recursive trigger |  | 
|  165 # programs (triggers that fire themselves) work. More specifically, |  | 
|  166 # this block focuses on recursive INSERT triggers. |  | 
|  167 # |  | 
|  168 do_test triggerC-2.1.0 { |  | 
|  169   execsql { |  | 
|  170     CREATE TABLE t2(a PRIMARY KEY); |  | 
|  171   } |  | 
|  172 } {} |  | 
|  173  |  | 
|  174 foreach {n tdefn rc} { |  | 
|  175   1 {  |  | 
|  176     CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN |  | 
|  177       INSERT INTO t2 VALUES(new.a - 1); |  | 
|  178     END;  |  | 
|  179   } {0 {10 9 8 7 6 5 4 3 2 1 0}} |  | 
|  180  |  | 
|  181   2 { |  | 
|  182     CREATE TRIGGER t2_trig AFTER INSERT ON t2 BEGIN |  | 
|  183       SELECT CASE WHEN new.a==2 THEN RAISE(IGNORE) ELSE NULL END; |  | 
|  184       INSERT INTO t2 VALUES(new.a - 1); |  | 
|  185     END; |  | 
|  186   } {0 {10 9 8 7 6 5 4 3 2}} |  | 
|  187  |  | 
|  188   3 {  |  | 
|  189     CREATE TRIGGER t2_trig BEFORE INSERT ON t2 WHEN (new.a>0) BEGIN |  | 
|  190       INSERT INTO t2 VALUES(new.a - 1); |  | 
|  191     END;  |  | 
|  192   } {0 {0 1 2 3 4 5 6 7 8 9 10}} |  | 
|  193  |  | 
|  194   4 {  |  | 
|  195     CREATE TRIGGER t2_trig BEFORE INSERT ON t2 BEGIN |  | 
|  196       SELECT CASE WHEN new.a==2 THEN RAISE(IGNORE) ELSE NULL END; |  | 
|  197       INSERT INTO t2 VALUES(new.a - 1); |  | 
|  198     END; |  | 
|  199   } {0 {3 4 5 6 7 8 9 10}} |  | 
|  200  |  | 
|  201   5 {  |  | 
|  202     CREATE TRIGGER t2_trig BEFORE INSERT ON t2 BEGIN |  | 
|  203       INSERT INTO t2 VALUES(new.a - 1); |  | 
|  204     END; |  | 
|  205   } {1 {too many levels of trigger recursion}} |  | 
|  206  |  | 
|  207   6 {  |  | 
|  208     CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN |  | 
|  209       INSERT OR IGNORE INTO t2 VALUES(new.a); |  | 
|  210     END; |  | 
|  211   } {0 10} |  | 
|  212  |  | 
|  213   7 {  |  | 
|  214     CREATE TRIGGER t2_trig BEFORE INSERT ON t2 WHEN (new.a>0) BEGIN |  | 
|  215       INSERT OR IGNORE INTO t2 VALUES(new.a); |  | 
|  216     END; |  | 
|  217   } {1 {too many levels of trigger recursion}} |  | 
|  218 } { |  | 
|  219   do_test triggerC-2.1.$n { |  | 
|  220     catchsql { DROP TRIGGER t2_trig } |  | 
|  221     execsql  { DELETE FROM t2 } |  | 
|  222     execsql  $tdefn |  | 
|  223     catchsql { |  | 
|  224       INSERT INTO t2 VALUES(10); |  | 
|  225       SELECT * FROM t2; |  | 
|  226     } |  | 
|  227   } $rc |  | 
|  228 } |  | 
|  229  |  | 
|  230 do_test triggerC-2.2 { |  | 
|  231   execsql { |  | 
|  232     CREATE TABLE t22(x); |  | 
|  233  |  | 
|  234     CREATE TRIGGER t22a AFTER INSERT ON t22 BEGIN |  | 
|  235       INSERT INTO t22 SELECT x + (SELECT max(x) FROM t22) FROM t22; |  | 
|  236     END; |  | 
|  237     CREATE TRIGGER t22b BEFORE INSERT ON t22 BEGIN |  | 
|  238       SELECT CASE WHEN (SELECT count(*) FROM t22) >= 100 |  | 
|  239                   THEN RAISE(IGNORE) |  | 
|  240                   ELSE NULL END; |  | 
|  241     END; |  | 
|  242  |  | 
|  243     INSERT INTO t22 VALUES(1); |  | 
|  244     SELECT count(*) FROM t22; |  | 
|  245   } |  | 
|  246 } {100} |  | 
|  247  |  | 
|  248 do_test triggerC-2.3 { |  | 
|  249   execsql { |  | 
|  250     CREATE TABLE t23(x PRIMARY KEY); |  | 
|  251  |  | 
|  252     CREATE TRIGGER t23a AFTER INSERT ON t23 BEGIN |  | 
|  253       INSERT INTO t23 VALUES(new.x + 1); |  | 
|  254     END; |  | 
|  255  |  | 
|  256     CREATE TRIGGER t23b BEFORE INSERT ON t23 BEGIN |  | 
|  257       SELECT CASE WHEN new.x>500 |  | 
|  258                   THEN RAISE(IGNORE) |  | 
|  259                   ELSE NULL END; |  | 
|  260     END; |  | 
|  261  |  | 
|  262     INSERT INTO t23 VALUES(1); |  | 
|  263     SELECT count(*) FROM t23; |  | 
|  264   } |  | 
|  265 } {500} |  | 
|  266   |  | 
|  267  |  | 
|  268 #----------------------------------------------------------------------- |  | 
|  269 # This block of tests, triggerC-3.*, test that SQLite throws an exception |  | 
|  270 # when it detects excessive recursion. |  | 
|  271 # |  | 
|  272 do_test triggerC-3.1.1 { |  | 
|  273   execsql { |  | 
|  274     CREATE TABLE t3(a, b); |  | 
|  275     CREATE TRIGGER t3i AFTER INSERT ON t3 BEGIN |  | 
|  276       DELETE FROM t3 WHERE rowid = new.rowid; |  | 
|  277     END; |  | 
|  278     CREATE TRIGGER t3d AFTER DELETE ON t3 BEGIN |  | 
|  279       INSERT INTO t3 VALUES(old.a, old.b); |  | 
|  280     END; |  | 
|  281   } |  | 
|  282 } {} |  | 
|  283 do_test triggerC-3.1.2 { |  | 
|  284   catchsql { INSERT INTO t3 VALUES(0,0) } |  | 
|  285 } {1 {too many levels of trigger recursion}} |  | 
|  286 do_test triggerC-3.1.3 { |  | 
|  287   execsql { SELECT * FROM t3 } |  | 
|  288 } {} |  | 
|  289  |  | 
|  290 do_test triggerC-3.2.1 { |  | 
|  291   execsql { |  | 
|  292     CREATE TABLE t3b(x); |  | 
|  293     CREATE TRIGGER t3bi AFTER INSERT ON t3b WHEN new.x<2000 BEGIN |  | 
|  294       INSERT INTO t3b VALUES(new.x+1); |  | 
|  295     END; |  | 
|  296   } |  | 
|  297   catchsql { |  | 
|  298     INSERT INTO t3b VALUES(1); |  | 
|  299   } |  | 
|  300 } {1 {too many levels of trigger recursion}} |  | 
|  301 do_test triggerC-3.2.2 { |  | 
|  302   db eval {SELECT * FROM t3b} |  | 
|  303 } {} |  | 
|  304  |  | 
|  305 do_test triggerC-3.3.1 { |  | 
|  306   catchsql { |  | 
|  307     INSERT INTO t3b VALUES(1000); |  | 
|  308   } |  | 
|  309 } {0 {}} |  | 
|  310 do_test triggerC-3.3.2 { |  | 
|  311   db eval {SELECT count(*), max(x), min(x) FROM t3b} |  | 
|  312 } {1001 2000 1000} |  | 
|  313  |  | 
|  314 do_test triggerC-3.4.1 { |  | 
|  315   catchsql { |  | 
|  316     DELETE FROM t3b; |  | 
|  317     INSERT INTO t3b VALUES(999); |  | 
|  318   } |  | 
|  319 } {1 {too many levels of trigger recursion}} |  | 
|  320 do_test triggerC-3.4.2 { |  | 
|  321   db eval {SELECT count(*), max(x), min(x) FROM t3b} |  | 
|  322 } {0 {} {}} |  | 
|  323  |  | 
|  324 do_test triggerC-3.5.1 { |  | 
|  325   sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 100 |  | 
|  326   catchsql { |  | 
|  327     INSERT INTO t3b VALUES(1900); |  | 
|  328   } |  | 
|  329 } {0 {}} |  | 
|  330 do_test triggerC-3.5.2 { |  | 
|  331   db eval {SELECT count(*), max(x), min(x) FROM t3b} |  | 
|  332 } {101 2000 1900} |  | 
|  333  |  | 
|  334 do_test triggerC-3.5.3 { |  | 
|  335   catchsql { |  | 
|  336     DELETE FROM t3b; |  | 
|  337     INSERT INTO t3b VALUES(1899); |  | 
|  338   } |  | 
|  339 } {1 {too many levels of trigger recursion}} |  | 
|  340 do_test triggerC-3.5.4 { |  | 
|  341   db eval {SELECT count(*), max(x), min(x) FROM t3b} |  | 
|  342 } {0 {} {}} |  | 
|  343  |  | 
|  344 do_test triggerC-3.6.1 { |  | 
|  345   sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1 |  | 
|  346   catchsql { |  | 
|  347     INSERT INTO t3b VALUES(1999); |  | 
|  348   } |  | 
|  349 } {0 {}} |  | 
|  350 do_test triggerC-3.6.2 { |  | 
|  351   db eval {SELECT count(*), max(x), min(x) FROM t3b} |  | 
|  352 } {2 2000 1999} |  | 
|  353  |  | 
|  354 do_test triggerC-3.6.3 { |  | 
|  355   catchsql { |  | 
|  356     DELETE FROM t3b; |  | 
|  357     INSERT INTO t3b VALUES(1998); |  | 
|  358   } |  | 
|  359 } {1 {too many levels of trigger recursion}} |  | 
|  360 do_test triggerC-3.6.4 { |  | 
|  361   db eval {SELECT count(*), max(x), min(x) FROM t3b} |  | 
|  362 } {0 {} {}} |  | 
|  363 sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000 |  | 
|  364        |  | 
|  365  |  | 
|  366 #----------------------------------------------------------------------- |  | 
|  367 # This next block of tests, triggerC-4.*, checks that affinity  |  | 
|  368 # transformations and constraint processing is performed at the correct  |  | 
|  369 # times relative to BEFORE and AFTER triggers. |  | 
|  370 # |  | 
|  371 # For an INSERT statement, for each row to be inserted: |  | 
|  372 # |  | 
|  373 #   1. Apply affinities to non-rowid values to be inserted. |  | 
|  374 #   2. Fire BEFORE triggers. |  | 
|  375 #   3. Process constraints. |  | 
|  376 #   4. Insert new record. |  | 
|  377 #   5. Fire AFTER triggers. |  | 
|  378 # |  | 
|  379 # If the value of the rowid field is to be automatically assigned, it is |  | 
|  380 # set to -1 in the new.* record. Even if it is explicitly set to NULL |  | 
|  381 # by the INSERT statement. |  | 
|  382 # |  | 
|  383 # For an UPDATE statement, for each row to be deleted: |  | 
|  384 # |  | 
|  385 #   1. Apply affinities to non-rowid values to be inserted. |  | 
|  386 #   2. Fire BEFORE triggers. |  | 
|  387 #   3. Process constraints. |  | 
|  388 #   4. Insert new record. |  | 
|  389 #   5. Fire AFTER triggers. |  | 
|  390 # |  | 
|  391 # For a DELETE statement, for each row to be deleted: |  | 
|  392 # |  | 
|  393 #   1. Fire BEFORE triggers. |  | 
|  394 #   2. Remove database record. |  | 
|  395 #   3. Fire AFTER triggers. |  | 
|  396 # |  | 
|  397 # When a numeric value that as an exact integer representation is stored |  | 
|  398 # in a column with REAL affinity, it is actually stored as an integer. |  | 
|  399 # These tests check that the typeof() such values is always 'real', |  | 
|  400 # not 'integer'. |  | 
|  401 # |  | 
|  402 # triggerC-4.1.*: Check that affinity transformations are made before |  | 
|  403 #                 triggers are invoked. |  | 
|  404 # |  | 
|  405 do_test triggerC-4.1.1 { |  | 
|  406   catchsql { DROP TABLE log } |  | 
|  407   catchsql { DROP TABLE t4 } |  | 
|  408   execsql { |  | 
|  409     CREATE TABLE log(t); |  | 
|  410     CREATE TABLE t4(a TEXT,b INTEGER,c REAL); |  | 
|  411     CREATE TRIGGER t4bi BEFORE INSERT ON t4 BEGIN |  | 
|  412       INSERT INTO log VALUES(new.rowid || ' ' || typeof(new.rowid) || ' ' || |  | 
|  413                              new.a     || ' ' || typeof(new.a)     || ' ' || |  | 
|  414                              new.b     || ' ' || typeof(new.b)     || ' ' || |  | 
|  415                              new.c     || ' ' || typeof(new.c) |  | 
|  416       ); |  | 
|  417     END; |  | 
|  418     CREATE TRIGGER t4ai AFTER INSERT ON t4 BEGIN |  | 
|  419       INSERT INTO log VALUES(new.rowid || ' ' || typeof(new.rowid) || ' ' || |  | 
|  420                              new.a     || ' ' || typeof(new.a)     || ' ' || |  | 
|  421                              new.b     || ' ' || typeof(new.b)     || ' ' || |  | 
|  422                              new.c     || ' ' || typeof(new.c) |  | 
|  423       ); |  | 
|  424     END; |  | 
|  425     CREATE TRIGGER t4bd BEFORE DELETE ON t4 BEGIN |  | 
|  426       INSERT INTO log VALUES(old.rowid || ' ' || typeof(old.rowid) || ' ' || |  | 
|  427                              old.a     || ' ' || typeof(old.a)     || ' ' || |  | 
|  428                              old.b     || ' ' || typeof(old.b)     || ' ' || |  | 
|  429                              old.c     || ' ' || typeof(old.c) |  | 
|  430       ); |  | 
|  431     END; |  | 
|  432     CREATE TRIGGER t4ad AFTER DELETE ON t4 BEGIN |  | 
|  433       INSERT INTO log VALUES(old.rowid || ' ' || typeof(old.rowid) || ' ' || |  | 
|  434                              old.a     || ' ' || typeof(old.a)     || ' ' || |  | 
|  435                              old.b     || ' ' || typeof(old.b)     || ' ' || |  | 
|  436                              old.c     || ' ' || typeof(old.c) |  | 
|  437       ); |  | 
|  438     END; |  | 
|  439     CREATE TRIGGER t4bu BEFORE UPDATE ON t4 BEGIN |  | 
|  440       INSERT INTO log VALUES(old.rowid || ' ' || typeof(old.rowid) || ' ' || |  | 
|  441                              old.a     || ' ' || typeof(old.a)     || ' ' || |  | 
|  442                              old.b     || ' ' || typeof(old.b)     || ' ' || |  | 
|  443                              old.c     || ' ' || typeof(old.c) |  | 
|  444       ); |  | 
|  445       INSERT INTO log VALUES(new.rowid || ' ' || typeof(new.rowid) || ' ' || |  | 
|  446                              new.a     || ' ' || typeof(new.a)     || ' ' || |  | 
|  447                              new.b     || ' ' || typeof(new.b)     || ' ' || |  | 
|  448                              new.c     || ' ' || typeof(new.c) |  | 
|  449       ); |  | 
|  450     END; |  | 
|  451     CREATE TRIGGER t4au AFTER UPDATE ON t4 BEGIN |  | 
|  452       INSERT INTO log VALUES(old.rowid || ' ' || typeof(old.rowid) || ' ' || |  | 
|  453                              old.a     || ' ' || typeof(old.a)     || ' ' || |  | 
|  454                              old.b     || ' ' || typeof(old.b)     || ' ' || |  | 
|  455                              old.c     || ' ' || typeof(old.c) |  | 
|  456       ); |  | 
|  457       INSERT INTO log VALUES(new.rowid || ' ' || typeof(new.rowid) || ' ' || |  | 
|  458                              new.a     || ' ' || typeof(new.a)     || ' ' || |  | 
|  459                              new.b     || ' ' || typeof(new.b)     || ' ' || |  | 
|  460                              new.c     || ' ' || typeof(new.c) |  | 
|  461       ); |  | 
|  462     END; |  | 
|  463   } |  | 
|  464 } {} |  | 
|  465 foreach {n insert log} { |  | 
|  466  |  | 
|  467   2 {  |  | 
|  468    INSERT INTO t4 VALUES('1', '1', '1'); |  | 
|  469    DELETE FROM t4; |  | 
|  470   } { |  | 
|  471     -1 integer 1 text 1 integer 1.0 real  |  | 
|  472      1 integer 1 text 1 integer 1.0 real |  | 
|  473      1 integer 1 text 1 integer 1.0 real  |  | 
|  474      1 integer 1 text 1 integer 1.0 real |  | 
|  475   } |  | 
|  476  |  | 
|  477   3 {  |  | 
|  478    INSERT INTO t4(rowid,a,b,c) VALUES(45, 45, 45, 45); |  | 
|  479    DELETE FROM t4; |  | 
|  480   } { |  | 
|  481     45 integer 45 text 45 integer 45.0 real |  | 
|  482     45 integer 45 text 45 integer 45.0 real |  | 
|  483     45 integer 45 text 45 integer 45.0 real |  | 
|  484     45 integer 45 text 45 integer 45.0 real |  | 
|  485   } |  | 
|  486  |  | 
|  487   4 {  |  | 
|  488    INSERT INTO t4(rowid,a,b,c) VALUES(-42.0, -42.0, -42.0, -42.0); |  | 
|  489    DELETE FROM t4; |  | 
|  490   } { |  | 
|  491     -42 integer -42.0 text -42 integer -42.0 real  |  | 
|  492     -42 integer -42.0 text -42 integer -42.0 real |  | 
|  493     -42 integer -42.0 text -42 integer -42.0 real  |  | 
|  494     -42 integer -42.0 text -42 integer -42.0 real |  | 
|  495   } |  | 
|  496  |  | 
|  497   5 {  |  | 
|  498    INSERT INTO t4(rowid,a,b,c) VALUES(NULL, -42.4, -42.4, -42.4); |  | 
|  499    DELETE FROM t4; |  | 
|  500   } { |  | 
|  501     -1 integer -42.4 text -42.4 real -42.4 real |  | 
|  502      1 integer -42.4 text -42.4 real -42.4 real |  | 
|  503      1 integer -42.4 text -42.4 real -42.4 real |  | 
|  504      1 integer -42.4 text -42.4 real -42.4 real |  | 
|  505   } |  | 
|  506  |  | 
|  507   6 {  |  | 
|  508    INSERT INTO t4 VALUES(7, 7, 7); |  | 
|  509    UPDATE t4 SET a=8, b=8, c=8; |  | 
|  510   } { |  | 
|  511     -1 integer 7 text 7 integer 7.0 real |  | 
|  512      1 integer 7 text 7 integer 7.0 real |  | 
|  513      1 integer 7 text 7 integer 7.0 real |  | 
|  514      1 integer 8 text 8 integer 8.0 real |  | 
|  515      1 integer 7 text 7 integer 7.0 real |  | 
|  516      1 integer 8 text 8 integer 8.0 real |  | 
|  517   } |  | 
|  518  |  | 
|  519   7 {  |  | 
|  520    UPDATE t4 SET rowid=2; |  | 
|  521   } { |  | 
|  522      1 integer 8 text 8 integer 8.0 real |  | 
|  523      2 integer 8 text 8 integer 8.0 real |  | 
|  524      1 integer 8 text 8 integer 8.0 real |  | 
|  525      2 integer 8 text 8 integer 8.0 real |  | 
|  526   } |  | 
|  527  |  | 
|  528   8 {  |  | 
|  529    UPDATE t4 SET a='9', b='9', c='9'; |  | 
|  530   } { |  | 
|  531      2 integer 8 text 8 integer 8.0 real |  | 
|  532      2 integer 9 text 9 integer 9.0 real |  | 
|  533      2 integer 8 text 8 integer 8.0 real |  | 
|  534      2 integer 9 text 9 integer 9.0 real |  | 
|  535   } |  | 
|  536  |  | 
|  537   9 {  |  | 
|  538    UPDATE t4 SET a='9.1', b='9.1', c='9.1'; |  | 
|  539   } { |  | 
|  540      2 integer 9   text 9   integer 9.0 real |  | 
|  541      2 integer 9.1 text 9.1 real    9.1 real |  | 
|  542      2 integer 9   text 9   integer 9.0 real |  | 
|  543      2 integer 9.1 text 9.1 real    9.1 real |  | 
|  544   } |  | 
|  545 } { |  | 
|  546   do_test triggerC-4.1.$n { |  | 
|  547     eval concat [execsql "  |  | 
|  548       DELETE FROM log; |  | 
|  549       $insert ;  |  | 
|  550       SELECT * FROM log; |  | 
|  551     "] |  | 
|  552   } [join $log " "] |  | 
|  553 }  |  | 
|  554  |  | 
|  555 #------------------------------------------------------------------------- |  | 
|  556 # This block of tests, triggerC-5.*, test that DELETE triggers are fired |  | 
|  557 # if a row is deleted as a result of OR REPLACE conflict resolution. |  | 
|  558 # |  | 
|  559 do_test triggerC-5.1.0 { |  | 
|  560   execsql { |  | 
|  561     DROP TABLE IF EXISTS t5; |  | 
|  562     CREATE TABLE t5(a INTEGER PRIMARY KEY, b); |  | 
|  563     CREATE UNIQUE INDEX t5i ON t5(b); |  | 
|  564     INSERT INTO t5 VALUES(1, 'a'); |  | 
|  565     INSERT INTO t5 VALUES(2, 'b'); |  | 
|  566     INSERT INTO t5 VALUES(3, 'c'); |  | 
|  567  |  | 
|  568     CREATE TABLE t5g(a, b, c); |  | 
|  569     CREATE TRIGGER t5t BEFORE DELETE ON t5 BEGIN |  | 
|  570       INSERT INTO t5g VALUES(old.a, old.b, (SELECT count(*) FROM t5)); |  | 
|  571     END; |  | 
|  572   } |  | 
|  573 } {} |  | 
|  574 foreach {n dml t5g t5} { |  | 
|  575   1 "DELETE FROM t5 WHERE a=2"                        {2 b 3} {1 a 3 c} |  | 
|  576   2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')"        {2 b 3} {1 a 2 d 3 c} |  | 
|  577   3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3"      {2 b 3} {1 a 2 c} |  | 
|  578   4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')"        {2 b 3} {1 a 3 c 4 b} |  | 
|  579   5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'"  {2 b 3} {1 a 3 b} |  | 
|  580   6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')"        {2 b 3 3 c 2} {1 a 2 c} |  | 
|  581   7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {1 a 3 2 b 2} {1 b} |  | 
|  582 } { |  | 
|  583   do_test triggerC-5.1.$n { |  | 
|  584     execsql " |  | 
|  585       BEGIN; |  | 
|  586         $dml ; |  | 
|  587         SELECT * FROM t5g; |  | 
|  588         SELECT * FROM t5; |  | 
|  589       ROLLBACK; |  | 
|  590     " |  | 
|  591   } [concat $t5g $t5] |  | 
|  592 } |  | 
|  593 do_test triggerC-5.2.0 { |  | 
|  594   execsql { |  | 
|  595     DROP TRIGGER t5t; |  | 
|  596     CREATE TRIGGER t5t AFTER DELETE ON t5 BEGIN |  | 
|  597       INSERT INTO t5g VALUES(old.a, old.b, (SELECT count(*) FROM t5)); |  | 
|  598     END; |  | 
|  599   } |  | 
|  600 } {} |  | 
|  601 foreach {n dml t5g t5} { |  | 
|  602   1 "DELETE FROM t5 WHERE a=2"                        {2 b 2} {1 a 3 c} |  | 
|  603   2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')"        {2 b 2} {1 a 2 d 3 c} |  | 
|  604   3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3"      {2 b 2} {1 a 2 c} |  | 
|  605   4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')"        {2 b 2} {1 a 3 c 4 b} |  | 
|  606   5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'"  {2 b 2} {1 a 3 b} |  | 
|  607   6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')"        {2 b 2 3 c 1} {1 a 2 c} |  | 
|  608   7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {1 a 2 2 b 1} {1 b} |  | 
|  609 } { |  | 
|  610   do_test triggerC-5.2.$n { |  | 
|  611     execsql " |  | 
|  612       BEGIN; |  | 
|  613         $dml ; |  | 
|  614         SELECT * FROM t5g; |  | 
|  615         SELECT * FROM t5; |  | 
|  616       ROLLBACK; |  | 
|  617     " |  | 
|  618   } [concat $t5g $t5] |  | 
|  619 } |  | 
|  620 do_test triggerC-5.3.0 { |  | 
|  621   execsql { PRAGMA recursive_triggers = off } |  | 
|  622 } {} |  | 
|  623 foreach {n dml t5g t5} { |  | 
|  624   1 "DELETE FROM t5 WHERE a=2"                        {2 b 2} {1 a 3 c} |  | 
|  625   2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')"        {} {1 a 2 d 3 c} |  | 
|  626   3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3"      {} {1 a 2 c} |  | 
|  627   4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')"        {} {1 a 3 c 4 b} |  | 
|  628   5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'"  {} {1 a 3 b} |  | 
|  629   6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')"        {} {1 a 2 c} |  | 
|  630   7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {} {1 b} |  | 
|  631 } { |  | 
|  632   do_test triggerC-5.3.$n { |  | 
|  633     execsql " |  | 
|  634       BEGIN; |  | 
|  635         $dml ; |  | 
|  636         SELECT * FROM t5g; |  | 
|  637         SELECT * FROM t5; |  | 
|  638       ROLLBACK; |  | 
|  639     " |  | 
|  640   } [concat $t5g $t5] |  | 
|  641 } |  | 
|  642 do_test triggerC-5.3.8 { |  | 
|  643   execsql { PRAGMA recursive_triggers = on } |  | 
|  644 } {} |  | 
|  645  |  | 
|  646 #------------------------------------------------------------------------- |  | 
|  647 # This block of tests, triggerC-6.*, tests that "PRAGMA recursive_triggers" |  | 
|  648 # statements return the current value of the recursive triggers flag. |  | 
|  649 # |  | 
|  650 do_test triggerC-6.1 { |  | 
|  651   execsql { PRAGMA recursive_triggers } |  | 
|  652 } {1} |  | 
|  653 do_test triggerC-6.2 { |  | 
|  654   execsql {  |  | 
|  655     PRAGMA recursive_triggers = off; |  | 
|  656     PRAGMA recursive_triggers; |  | 
|  657   } |  | 
|  658 } {0} |  | 
|  659 do_test triggerC-6.3 { |  | 
|  660   execsql {  |  | 
|  661     PRAGMA recursive_triggers = on; |  | 
|  662     PRAGMA recursive_triggers; |  | 
|  663   } |  | 
|  664 } {1} |  | 
|  665  |  | 
|  666 #------------------------------------------------------------------------- |  | 
|  667 # Test some of the "undefined behaviour" associated with triggers. The |  | 
|  668 # undefined behaviour occurs when a row being updated or deleted is  |  | 
|  669 # manipulated by a BEFORE trigger. |  | 
|  670 #  |  | 
|  671 do_test triggerC-7.1 { |  | 
|  672   execsql { |  | 
|  673     CREATE TABLE t8(x); |  | 
|  674     CREATE TABLE t7(a, b); |  | 
|  675     INSERT INTO t7 VALUES(1, 2); |  | 
|  676     INSERT INTO t7 VALUES(3, 4); |  | 
|  677     INSERT INTO t7 VALUES(5, 6); |  | 
|  678     CREATE TRIGGER t7t BEFORE UPDATE ON t7 BEGIN |  | 
|  679       DELETE FROM t7 WHERE a = 1; |  | 
|  680     END; |  | 
|  681     CREATE TRIGGER t7ta AFTER UPDATE ON t7 BEGIN |  | 
|  682       INSERT INTO t8 VALUES('after fired ' || old.rowid || '->' || new.rowid); |  | 
|  683     END; |  | 
|  684   } |  | 
|  685 } {} |  | 
|  686 do_test triggerC-7.2 { |  | 
|  687   execsql { |  | 
|  688     BEGIN; |  | 
|  689       UPDATE t7 SET b=7 WHERE a = 5; |  | 
|  690       SELECT * FROM t7; |  | 
|  691       SELECT * FROM t8; |  | 
|  692     ROLLBACK; |  | 
|  693   } |  | 
|  694 } {3 4 5 7 {after fired 3->3}} |  | 
|  695 do_test triggerC-7.3 { |  | 
|  696   execsql { |  | 
|  697     BEGIN; |  | 
|  698       UPDATE t7 SET b=7 WHERE a = 1; |  | 
|  699       SELECT * FROM t7; |  | 
|  700       SELECT * FROM t8; |  | 
|  701     ROLLBACK; |  | 
|  702   } |  | 
|  703 } {3 4 5 6} |  | 
|  704  |  | 
|  705 do_test triggerC-7.4 { |  | 
|  706   execsql { |  | 
|  707     DROP TRIGGER t7t; |  | 
|  708     CREATE TRIGGER t7t BEFORE UPDATE ON t7 WHEN (old.rowid!=1 OR new.rowid!=8) |  | 
|  709     BEGIN |  | 
|  710       UPDATE t7 set rowid = 8 WHERE rowid=1; |  | 
|  711     END; |  | 
|  712   } |  | 
|  713 } {} |  | 
|  714 do_test triggerC-7.5 { |  | 
|  715   execsql { |  | 
|  716     BEGIN; |  | 
|  717       UPDATE t7 SET b=7 WHERE a = 5; |  | 
|  718       SELECT rowid, * FROM t7; |  | 
|  719       SELECT * FROM t8; |  | 
|  720     ROLLBACK; |  | 
|  721   } |  | 
|  722 } {2 3 4 3 5 7 8 1 2 {after fired 1->8} {after fired 3->3}} |  | 
|  723 do_test triggerC-7.6 { |  | 
|  724   execsql { |  | 
|  725     BEGIN; |  | 
|  726       UPDATE t7 SET b=7 WHERE a = 1; |  | 
|  727       SELECT rowid, * FROM t7; |  | 
|  728       SELECT * FROM t8; |  | 
|  729     ROLLBACK; |  | 
|  730   } |  | 
|  731 } {2 3 4 3 5 6 8 1 2 {after fired 1->8}} |  | 
|  732  |  | 
|  733 do_test triggerC-7.7 { |  | 
|  734   execsql { |  | 
|  735     DROP TRIGGER t7t; |  | 
|  736     DROP TRIGGER t7ta; |  | 
|  737     CREATE TRIGGER t7t BEFORE DELETE ON t7 BEGIN |  | 
|  738       UPDATE t7 set rowid = 8 WHERE rowid=1; |  | 
|  739     END; |  | 
|  740     CREATE TRIGGER t7ta AFTER DELETE ON t7 BEGIN |  | 
|  741       INSERT INTO t8 VALUES('after fired ' || old.rowid); |  | 
|  742     END; |  | 
|  743   } |  | 
|  744 } {} |  | 
|  745 do_test triggerC-7.8 { |  | 
|  746   execsql { |  | 
|  747     BEGIN; |  | 
|  748       DELETE FROM t7 WHERE a = 3; |  | 
|  749       SELECT rowid, * FROM t7; |  | 
|  750       SELECT * FROM t8; |  | 
|  751     ROLLBACK; |  | 
|  752   } |  | 
|  753 } {3 5 6 8 1 2 {after fired 2}} |  | 
|  754 do_test triggerC-7.9 { |  | 
|  755   execsql { |  | 
|  756     BEGIN; |  | 
|  757       DELETE FROM t7 WHERE a = 1; |  | 
|  758       SELECT rowid, * FROM t7; |  | 
|  759       SELECT * FROM t8; |  | 
|  760     ROLLBACK; |  | 
|  761   } |  | 
|  762 } {2 3 4 3 5 6 8 1 2} |  | 
|  763  |  | 
|  764  |  | 
|  765 finish_test |  | 
| OLD | NEW |