000001  # 2011 October 28
000002  #
000003  # The author disclaims copyright to this source code.  In place of
000004  # a legal notice, here is a blessing:
000005  #
000006  #    May you do good and not evil.
000007  #    May you find forgiveness for yourself and forgive others.
000008  #    May you share freely, never taking more than you give.
000009  #
000010  #***********************************************************************
000011  #
000012  
000013  set testdir [file dirname $argv0]
000014  source $testdir/tester.tcl
000015  set testprefix e_changes
000016  
000017  # Like [do_execsql_test], except it appends the value returned by 
000018  # [db changes] to the result of executing the SQL script.
000019  #
000020  proc do_changes_test {tn sql res} {
000021    uplevel [list \
000022      do_test $tn "concat \[execsql {$sql}\] \[db changes\]" $res
000023    ]
000024  }
000025  
000026  
000027  #--------------------------------------------------------------------------
000028  # EVIDENCE-OF: R-58361-29089 The changes() function returns the number
000029  # of database rows that were changed or inserted or deleted by the most
000030  # recently completed INSERT, DELETE, or UPDATE statement, exclusive of
000031  # statements in lower-level triggers.
000032  #
000033  do_execsql_test 1.0 {
000034    CREATE TABLE t1(a, b);
000035    CREATE TABLE t2(x, y, PRIMARY KEY(x, y)) WITHOUT ROWID;
000036    CREATE INDEX i1 ON t1(a);
000037    CREATE INDEX i2 ON t2(y);
000038  }
000039  foreach {tn schema} {
000040    1 { 
000041        CREATE TABLE t1(a, b);
000042        CREATE INDEX i1 ON t1(b);
000043    }
000044    2 { 
000045        CREATE TABLE t1(a, b, PRIMARY KEY(a, b)) WITHOUT ROWID;
000046        CREATE INDEX i1 ON t1(b);
000047    }
000048  } {
000049    reset_db
000050    execsql $schema
000051  
000052    # Insert 1 row.
000053    do_changes_test 1.$tn.1 { INSERT INTO t1 VALUES(0, 0) } 1
000054  
000055    # Insert 10 rows.
000056    do_changes_test 1.$tn.2 {
000057      WITH rows(i, j) AS (
000058          SELECT 1, 1 UNION ALL SELECT i+1, j+i FROM rows WHERE i<10
000059      )
000060      INSERT INTO t1 SELECT * FROM rows
000061    } 10
000062  
000063    # Modify 5 rows.
000064    do_changes_test 1.$tn.3 {
000065      UPDATE t1 SET b=b+1 WHERE a<5;
000066    } 5
000067  
000068    # Delete 4 rows
000069    do_changes_test 1.$tn.4 {
000070      DELETE FROM t1 WHERE a>6
000071    } 4
000072  
000073    # Check the "on the database connecton specified" part of hte
000074    # requirement - changes made by other connections do not show up in
000075    # the return value of sqlite3_changes().
000076    do_test 1.$tn.5 {
000077      sqlite3 db2 test.db
000078      execsql { INSERT INTO t1 VALUES(-1, -1) } db2
000079      db2 changes
000080    } 1
000081    do_test 1.$tn.6 {
000082      db changes
000083    } 4
000084    db2 close
000085  
000086    # Test that statements that modify no rows because they hit UNIQUE
000087    # constraints set the sqlite3_changes() value to 0. Regardless of
000088    # whether or not they are executed inside an explicit transaction.
000089    #
000090    #   1.$tn.8-9: outside of a transaction
000091    #   1.$tn.10-12: inside a transaction
000092    #
000093    do_changes_test 1.$tn.7 {
000094      CREATE UNIQUE INDEX i2 ON t1(a);
000095    } 4
000096    do_catchsql_test 1.$tn.8 {
000097      INSERT INTO t1 VALUES('a', 0), ('b', 0), ('c', 0), (0, 11);
000098    } {1 {UNIQUE constraint failed: t1.a}}
000099    do_test 1.$tn.9 { db changes } 0
000100    do_catchsql_test 1.$tn.10 {
000101      BEGIN;
000102        INSERT INTO t1 VALUES('a', 0), ('b', 0), ('c', 0), (0, 11);
000103    } {1 {UNIQUE constraint failed: t1.a}}
000104    do_test 1.$tn.11 { db changes } 0
000105    do_changes_test 1.$tn.12 COMMIT 0
000106  
000107  }
000108  
000109  
000110  #--------------------------------------------------------------------------
000111  # X-EVIDENCE-OF: R-44877-05564 Executing any other type of SQL statement
000112  # does not modify the value returned by this function.
000113  #
000114  reset_db
000115  do_changes_test 2.1 { CREATE TABLE t1(x)          } 0
000116  do_changes_test 2.2 { 
000117    WITH d(y) AS (SELECT 1 UNION ALL SELECT y+1 FROM d WHERE y<47)
000118    INSERT INTO t1 SELECT y FROM d;
000119  } 47
000120  
000121  # The statement above set changes() to 47. Check that none of the following
000122  # modify this.
000123  do_changes_test 2.3 { SELECT count(x) FROM t1 } {47 47}
000124  do_changes_test 2.4 { DROP TABLE t1               } 47
000125  do_changes_test 2.5 { CREATE TABLE t1(x)          } 47
000126  ifcapable altertable {
000127    do_changes_test 2.6 { ALTER TABLE t1 ADD COLUMN b } 47
000128  }
000129  
000130  
000131  #--------------------------------------------------------------------------
000132  # EVIDENCE-OF: R-53938-27527 Only changes made directly by the INSERT,
000133  # UPDATE or DELETE statement are considered - auxiliary changes caused
000134  # by triggers, foreign key actions or REPLACE constraint resolution are
000135  # not counted.
000136  #
000137  #   3.1.*: triggers
000138  #   3.2.*: foreign key actions
000139  #   3.3.*: replace constraints
000140  #
000141  reset_db
000142  do_execsql_test 3.1.0 {
000143    CREATE TABLE log(x);
000144    CREATE TABLE p1(one PRIMARY KEY, two);
000145  
000146    CREATE TRIGGER tr_ai AFTER INSERT ON p1 BEGIN
000147      INSERT INTO log VALUES('insert');
000148    END;
000149    CREATE TRIGGER tr_bd BEFORE DELETE ON p1 BEGIN
000150      INSERT INTO log VALUES('delete');
000151    END;
000152    CREATE TRIGGER tr_au AFTER UPDATE ON p1 BEGIN
000153      INSERT INTO log VALUES('update');
000154    END;
000155  
000156  }
000157  
000158  do_changes_test 3.1.1 {
000159    INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C');
000160  } 3
000161  do_changes_test 3.1.2 {
000162    UPDATE p1 SET two = two||two;
000163  } 3
000164  do_changes_test 3.1.3 {
000165    DELETE FROM p1 WHERE one IN ('a', 'c');
000166  } 2
000167  do_execsql_test 3.1.4 {
000168    -- None of the inserts on table log were counted.
000169    SELECT count(*) FROM log
000170  } 8
000171  
000172  do_execsql_test 3.2.0 {
000173    DELETE FROM p1;
000174    INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C');
000175  
000176    CREATE TABLE c1(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET NULL);
000177    CREATE TABLE c2(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET DEFAULT);
000178    CREATE TABLE c3(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE CASCADE);
000179    INSERT INTO c1 VALUES('a', 'aaa');
000180    INSERT INTO c2 VALUES('b', 'bbb');
000181    INSERT INTO c3 VALUES('c', 'ccc');
000182  
000183    INSERT INTO p1 VALUES('d', 'D'), ('e', 'E'), ('f', 'F');
000184    CREATE TABLE c4(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET NULL);
000185    CREATE TABLE c5(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET DEFAULT);
000186    CREATE TABLE c6(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE CASCADE);
000187    INSERT INTO c4 VALUES('d', 'aaa');
000188    INSERT INTO c5 VALUES('e', 'bbb');
000189    INSERT INTO c6 VALUES('f', 'ccc');
000190  
000191    PRAGMA foreign_keys = ON;
000192  }
000193  
000194  do_changes_test 3.2.1 { DELETE FROM p1 WHERE one = 'a' } 1
000195  do_changes_test 3.2.2 { DELETE FROM p1 WHERE one = 'b' } 1
000196  do_changes_test 3.2.3 { DELETE FROM p1 WHERE one = 'c' } 1
000197  do_execsql_test 3.2.4 { 
000198    SELECT * FROM c1;
000199    SELECT * FROM c2;
000200    SELECT * FROM c3;
000201  } {{} aaa {} bbb}
000202  
000203  do_changes_test 3.2.5 { UPDATE p1 SET one = 'g' WHERE one = 'd' } 1
000204  do_changes_test 3.2.6 { UPDATE p1 SET one = 'h' WHERE one = 'e' } 1
000205  do_changes_test 3.2.7 { UPDATE p1 SET one = 'i' WHERE one = 'f' } 1
000206  do_execsql_test 3.2.8 { 
000207    SELECT * FROM c4;
000208    SELECT * FROM c5;
000209    SELECT * FROM c6;
000210  } {{} aaa {} bbb i ccc}
000211  
000212  do_execsql_test 3.3.0 {
000213    CREATE TABLE r1(a UNIQUE, b UNIQUE);
000214    INSERT INTO r1 VALUES('i', 'i');
000215    INSERT INTO r1 VALUES('ii', 'ii');
000216    INSERT INTO r1 VALUES('iii', 'iii');
000217    INSERT INTO r1 VALUES('iv', 'iv');
000218    INSERT INTO r1 VALUES('v', 'v');
000219    INSERT INTO r1 VALUES('vi', 'vi');
000220    INSERT INTO r1 VALUES('vii', 'vii');
000221  }
000222  
000223  do_changes_test 3.3.1 { INSERT OR REPLACE INTO r1 VALUES('i', 1)    }   1
000224  do_changes_test 3.3.2 { INSERT OR REPLACE INTO r1 VALUES('iv', 'v') }   1
000225  do_changes_test 3.3.3 { UPDATE OR REPLACE r1 SET b='v' WHERE a='iii' }  1
000226  do_changes_test 3.3.4 { UPDATE OR REPLACE r1 SET b='vi',a='vii' WHERE a='ii' } 1
000227  do_execsql_test 3.3.5 { 
000228    SELECT * FROM r1 ORDER BY a;
000229  } {i 1   iii v   vii vi}
000230  
000231  
000232  #--------------------------------------------------------------------------
000233  # EVIDENCE-OF: R-09813-48563 The value returned by sqlite3_changes()
000234  # immediately after an INSERT, UPDATE or DELETE statement run on a view
000235  # is always zero.
000236  #
000237  reset_db
000238  do_execsql_test 4.1 {
000239    CREATE TABLE log(log);
000240    CREATE TABLE t1(x, y);
000241    INSERT INTO t1 VALUES(1, 2);
000242    INSERT INTO t1 VALUES(3, 4);
000243    INSERT INTO t1 VALUES(5, 6);
000244  
000245    CREATE VIEW v1 AS SELECT * FROM t1;
000246    CREATE TRIGGER v1_i INSTEAD OF INSERT ON v1 BEGIN
000247      INSERT INTO log VALUES('insert');
000248    END;
000249    CREATE TRIGGER v1_u INSTEAD OF UPDATE ON v1 BEGIN
000250      INSERT INTO log VALUES('update'), ('update');
000251    END;
000252    CREATE TRIGGER v1_d INSTEAD OF DELETE ON v1 BEGIN
000253      INSERT INTO log VALUES('delete'), ('delete'), ('delete');
000254    END;
000255  }
000256  
000257  do_changes_test 4.2.1 { INSERT INTO t1 SELECT * FROM t1 }  3
000258  do_changes_test 4.2.2 { INSERT INTO v1 VALUES(1, 2) }      0
000259  
000260  do_changes_test 4.3.1 { INSERT INTO t1 SELECT * FROM t1 }  6
000261  do_changes_test 4.3.2 { UPDATE v1 SET y='xyz' WHERE x=1 }  0
000262  
000263  do_changes_test 4.4.1 { INSERT INTO t1 SELECT * FROM t1 } 12
000264  do_changes_test 4.4.2 { DELETE FROM v1 WHERE x=5 }         0
000265  
000266  
000267  #--------------------------------------------------------------------------
000268  # EVIDENCE-OF: R-32918-61474 Before entering a trigger program the value
000269  # returned by sqlite3_changes() function is saved. After the trigger
000270  # program has finished, the original value is restored.
000271  #
000272  reset_db
000273  db func my_changes my_changes
000274  set ::changes [list]
000275  proc my_changes {x} {
000276    set res [db changes]
000277    lappend ::changes $x $res
000278    return $res
000279  }
000280  
000281  do_execsql_test 5.1.0 {
000282    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
000283    CREATE TABLE t2(x);
000284    INSERT INTO t1 VALUES(1, NULL);
000285    INSERT INTO t1 VALUES(2, NULL);
000286    INSERT INTO t1 VALUES(3, NULL);
000287    CREATE TRIGGER AFTER UPDATE ON t1 BEGIN
000288      INSERT INTO t2 VALUES('a'), ('b'), ('c');
000289      SELECT my_changes('trigger');
000290    END;
000291  }
000292  
000293  do_execsql_test 5.1.1 {
000294    INSERT INTO t2 VALUES('a'), ('b');
000295    UPDATE t1 SET b = my_changes('update');
000296    SELECT * FROM t1;
000297  } {1 2 2 2 3 2}
000298  
000299  # Value is being restored to "2" when the trigger program exits.
000300  do_test 5.1.2 {
000301    set ::changes
000302  } {update 2 trigger 3 update 2 trigger 3 update 2 trigger 3}
000303  
000304  
000305  reset_db
000306  do_execsql_test 5.2.0 {
000307    CREATE TABLE t1(a, b);
000308    CREATE TABLE log(x);
000309    INSERT INTO t1 VALUES(1, 0);
000310    INSERT INTO t1 VALUES(2, 0);
000311    INSERT INTO t1 VALUES(3, 0);
000312    CREATE TRIGGER t1_a_u AFTER UPDATE ON t1 BEGIN
000313      INSERT INTO log VALUES(old.b || ' -> ' || new.b || ' c = ' || changes() );
000314    END;
000315    CREATE TABLE t2(a);
000316    INSERT INTO t2 VALUES(1), (2), (3);
000317    UPDATE t1 SET b = changes();
000318  }
000319  do_execsql_test 5.2.1 {
000320    SELECT * FROM t1;
000321  } {1 3 2 3 3 3}
000322  do_execsql_test 5.2.2 {
000323    SELECT * FROM log;
000324  } {{0 -> 3 c = 3} {0 -> 3 c = 3} {0 -> 3 c = 3}}
000325  
000326  
000327  #--------------------------------------------------------------------------
000328  # EVIDENCE-OF: R-17146-37073 Within a trigger program each INSERT,
000329  # UPDATE and DELETE statement sets the value returned by
000330  # sqlite3_changes() upon completion as normal. Of course, this value
000331  # will not include any changes performed by sub-triggers, as the
000332  # sqlite3_changes() value will be saved and restored after each
000333  # sub-trigger has run.
000334  reset_db
000335  do_execsql_test 6.0 {
000336  
000337    CREATE TABLE t1(a, b);
000338    CREATE TABLE t2(a, b);
000339    CREATE TABLE t3(a, b);
000340    CREATE TABLE log(x);
000341  
000342    CREATE TRIGGER t1_i BEFORE INSERT ON t1 BEGIN
000343      INSERT INTO t2 VALUES(new.a, new.b), (new.a, new.b);
000344      INSERT INTO log VALUES('t2->' || changes());
000345    END;
000346  
000347    CREATE TRIGGER t2_i AFTER INSERT ON t2 BEGIN
000348      INSERT INTO t3 VALUES(new.a, new.b), (new.a, new.b), (new.a, new.b);
000349      INSERT INTO log VALUES('t3->' || changes());
000350    END;
000351  
000352    CREATE TRIGGER t1_u AFTER UPDATE ON t1 BEGIN
000353      UPDATE t2 SET b=new.b WHERE a=old.a;
000354      INSERT INTO log VALUES('t2->' || changes());
000355    END;
000356  
000357    CREATE TRIGGER t2_u BEFORE UPDATE ON t2 BEGIN
000358      UPDATE t3 SET b=new.b WHERE a=old.a;
000359      INSERT INTO log VALUES('t3->' || changes());
000360    END;
000361  
000362    CREATE TRIGGER t1_d AFTER DELETE ON t1 BEGIN
000363      DELETE FROM t2 WHERE a=old.a AND b=old.b;
000364      INSERT INTO log VALUES('t2->' || changes());
000365    END;
000366  
000367    CREATE TRIGGER t2_d BEFORE DELETE ON t2 BEGIN
000368      DELETE FROM t3 WHERE a=old.a AND b=old.b;
000369      INSERT INTO log VALUES('t3->' || changes());
000370    END;
000371  }
000372  
000373  do_changes_test 6.1 {
000374    INSERT INTO t1 VALUES('+', 'o');
000375    SELECT * FROM log;
000376  } {t3->3 t3->3 t2->2 1}
000377  
000378  do_changes_test 6.2 {
000379    DELETE FROM log;
000380    UPDATE t1 SET b='*';
000381    SELECT * FROM log;
000382  } {t3->6 t3->6 t2->2 1}
000383  
000384  do_changes_test 6.3 {
000385    DELETE FROM log;
000386    DELETE FROM t1;
000387    SELECT * FROM log;
000388  } {t3->6 t3->0 t2->2 1}
000389  
000390  
000391  #--------------------------------------------------------------------------
000392  # EVIDENCE-OF: R-43399-09409 This means that if the changes() SQL
000393  # function (or similar) is used by the first INSERT, UPDATE or DELETE
000394  # statement within a trigger, it returns the value as set when the
000395  # calling statement began executing.
000396  #
000397  # EVIDENCE-OF: R-53215-27584 If it is used by the second or subsequent
000398  # such statement within a trigger program, the value returned reflects
000399  # the number of rows modified by the previous INSERT, UPDATE or DELETE
000400  # statement within the same trigger.
000401  #
000402  reset_db
000403  do_execsql_test 7.1 {
000404    CREATE TABLE q1(t);
000405    CREATE TABLE q2(u, v);
000406    CREATE TABLE q3(w);
000407  
000408    CREATE TRIGGER q2_insert BEFORE INSERT ON q2 BEGIN
000409  
000410      /* changes() returns value from previous I/U/D in callers context */
000411      INSERT INTO q1 VALUES('1:' || changes());
000412  
000413      /* changes() returns value of previous I/U/D in this context */
000414      INSERT INTO q3 VALUES(changes()), (2), (3);
000415      INSERT INTO q1 VALUES('2:' || changes());
000416      INSERT INTO q3 VALUES(changes() + 3), (changes()+4);
000417      SELECT 'this does not affect things!';
000418      INSERT INTO q1 VALUES('3:' || changes());
000419      UPDATE q3 SET w = w+10 WHERE w%2;
000420      INSERT INTO q1 VALUES('4:' || changes());
000421      DELETE FROM q3;
000422      INSERT INTO q1 VALUES('5:' || changes());
000423    END;
000424  }
000425  
000426  do_execsql_test 7.2 {
000427    INSERT INTO q2 VALUES('x', 'y');
000428    SELECT * FROM q1;
000429  } {
000430    1:0   2:3   3:2   4:3   5:5
000431  }
000432  
000433  do_execsql_test 7.3 {
000434    DELETE FROM q1;
000435    INSERT INTO q2 VALUES('x', 'y');
000436    SELECT * FROM q1;
000437  } {
000438    1:5   2:3   3:2   4:3   5:5
000439  }
000440  
000441  
000442  
000443  finish_test