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