resolve upstream merge conflict in distclean
[sqlcipher.git] / test / e_changes.test
blob2eb77d3130eb36d78757560e7b003e70051e7688
1 # 2011 October 28
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 #***********************************************************************
13 set testdir [file dirname $argv0]
14 source $testdir/tester.tcl
15 set testprefix e_changes
17 # Like [do_execsql_test], except it appends the value returned by 
18 # [db changes] to the result of executing the SQL script.
20 proc do_changes_test {tn sql res} {
21   uplevel [list \
22     do_test $tn "concat \[execsql {$sql}\] \[db changes\]" $res
23   ]
27 #--------------------------------------------------------------------------
28 # EVIDENCE-OF: R-58361-29089 The changes() function returns the number
29 # of database rows that were changed or inserted or deleted by the most
30 # recently completed INSERT, DELETE, or UPDATE statement, exclusive of
31 # statements in lower-level triggers.
33 do_execsql_test 1.0 {
34   CREATE TABLE t1(a, b);
35   CREATE TABLE t2(x, y, PRIMARY KEY(x, y)) WITHOUT ROWID;
36   CREATE INDEX i1 ON t1(a);
37   CREATE INDEX i2 ON t2(y);
39 foreach {tn schema} {
40   1 { 
41       CREATE TABLE t1(a, b);
42       CREATE INDEX i1 ON t1(b);
43   }
44   2 { 
45       CREATE TABLE t1(a, b, PRIMARY KEY(a, b)) WITHOUT ROWID;
46       CREATE INDEX i1 ON t1(b);
47   }
48 } {
49   reset_db
50   execsql $schema
52   # Insert 1 row.
53   do_changes_test 1.$tn.1 { INSERT INTO t1 VALUES(0, 0) } 1
55   # Insert 10 rows.
56   do_changes_test 1.$tn.2 {
57     WITH rows(i, j) AS (
58         SELECT 1, 1 UNION ALL SELECT i+1, j+i FROM rows WHERE i<10
59     )
60     INSERT INTO t1 SELECT * FROM rows
61   } 10
63   # Modify 5 rows.
64   do_changes_test 1.$tn.3 {
65     UPDATE t1 SET b=b+1 WHERE a<5;
66   } 5
68   # Delete 4 rows
69   do_changes_test 1.$tn.4 {
70     DELETE FROM t1 WHERE a>6
71   } 4
73   # Check the "on the database connecton specified" part of hte
74   # requirement - changes made by other connections do not show up in
75   # the return value of sqlite3_changes().
76   do_test 1.$tn.5 {
77     sqlite3 db2 test.db
78     execsql { INSERT INTO t1 VALUES(-1, -1) } db2
79     db2 changes
80   } 1
81   do_test 1.$tn.6 {
82     db changes
83   } 4
84   db2 close
86   # Test that statements that modify no rows because they hit UNIQUE
87   # constraints set the sqlite3_changes() value to 0. Regardless of
88   # whether or not they are executed inside an explicit transaction.
89   #
90   #   1.$tn.8-9: outside of a transaction
91   #   1.$tn.10-12: inside a transaction
92   #
93   do_changes_test 1.$tn.7 {
94     CREATE UNIQUE INDEX i2 ON t1(a);
95   } 4
96   do_catchsql_test 1.$tn.8 {
97     INSERT INTO t1 VALUES('a', 0), ('b', 0), ('c', 0), (0, 11);
98   } {1 {UNIQUE constraint failed: t1.a}}
99   do_test 1.$tn.9 { db changes } 0
100   do_catchsql_test 1.$tn.10 {
101     BEGIN;
102       INSERT INTO t1 VALUES('a', 0), ('b', 0), ('c', 0), (0, 11);
103   } {1 {UNIQUE constraint failed: t1.a}}
104   do_test 1.$tn.11 { db changes } 0
105   do_changes_test 1.$tn.12 COMMIT 0
110 #--------------------------------------------------------------------------
111 # X-EVIDENCE-OF: R-44877-05564 Executing any other type of SQL statement
112 # does not modify the value returned by this function.
114 reset_db
115 do_changes_test 2.1 { CREATE TABLE t1(x)          } 0
116 do_changes_test 2.2 { 
117   WITH d(y) AS (SELECT 1 UNION ALL SELECT y+1 FROM d WHERE y<47)
118   INSERT INTO t1 SELECT y FROM d;
119 } 47
121 # The statement above set changes() to 47. Check that none of the following
122 # modify this.
123 do_changes_test 2.3 { SELECT count(x) FROM t1 } {47 47}
124 do_changes_test 2.4 { DROP TABLE t1               } 47
125 do_changes_test 2.5 { CREATE TABLE t1(x)          } 47
126 ifcapable altertable {
127   do_changes_test 2.6 { ALTER TABLE t1 ADD COLUMN b } 47
131 #--------------------------------------------------------------------------
132 # EVIDENCE-OF: R-53938-27527 Only changes made directly by the INSERT,
133 # UPDATE or DELETE statement are considered - auxiliary changes caused
134 # by triggers, foreign key actions or REPLACE constraint resolution are
135 # not counted.
137 #   3.1.*: triggers
138 #   3.2.*: foreign key actions
139 #   3.3.*: replace constraints
141 reset_db
142 do_execsql_test 3.1.0 {
143   CREATE TABLE log(x);
144   CREATE TABLE p1(one PRIMARY KEY, two);
146   CREATE TRIGGER tr_ai AFTER INSERT ON p1 BEGIN
147     INSERT INTO log VALUES('insert');
148   END;
149   CREATE TRIGGER tr_bd BEFORE DELETE ON p1 BEGIN
150     INSERT INTO log VALUES('delete');
151   END;
152   CREATE TRIGGER tr_au AFTER UPDATE ON p1 BEGIN
153     INSERT INTO log VALUES('update');
154   END;
158 do_changes_test 3.1.1 {
159   INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C');
160 } 3
161 do_changes_test 3.1.2 {
162   UPDATE p1 SET two = two||two;
163 } 3
164 do_changes_test 3.1.3 {
165   DELETE FROM p1 WHERE one IN ('a', 'c');
166 } 2
167 do_execsql_test 3.1.4 {
168   -- None of the inserts on table log were counted.
169   SELECT count(*) FROM log
170 } 8
172 do_execsql_test 3.2.0 {
173   DELETE FROM p1;
174   INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C');
176   CREATE TABLE c1(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET NULL);
177   CREATE TABLE c2(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET DEFAULT);
178   CREATE TABLE c3(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE CASCADE);
179   INSERT INTO c1 VALUES('a', 'aaa');
180   INSERT INTO c2 VALUES('b', 'bbb');
181   INSERT INTO c3 VALUES('c', 'ccc');
183   INSERT INTO p1 VALUES('d', 'D'), ('e', 'E'), ('f', 'F');
184   CREATE TABLE c4(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET NULL);
185   CREATE TABLE c5(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET DEFAULT);
186   CREATE TABLE c6(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE CASCADE);
187   INSERT INTO c4 VALUES('d', 'aaa');
188   INSERT INTO c5 VALUES('e', 'bbb');
189   INSERT INTO c6 VALUES('f', 'ccc');
191   PRAGMA foreign_keys = ON;
194 do_changes_test 3.2.1 { DELETE FROM p1 WHERE one = 'a' } 1
195 do_changes_test 3.2.2 { DELETE FROM p1 WHERE one = 'b' } 1
196 do_changes_test 3.2.3 { DELETE FROM p1 WHERE one = 'c' } 1
197 do_execsql_test 3.2.4 { 
198   SELECT * FROM c1;
199   SELECT * FROM c2;
200   SELECT * FROM c3;
201 } {{} aaa {} bbb}
203 do_changes_test 3.2.5 { UPDATE p1 SET one = 'g' WHERE one = 'd' } 1
204 do_changes_test 3.2.6 { UPDATE p1 SET one = 'h' WHERE one = 'e' } 1
205 do_changes_test 3.2.7 { UPDATE p1 SET one = 'i' WHERE one = 'f' } 1
206 do_execsql_test 3.2.8 { 
207   SELECT * FROM c4;
208   SELECT * FROM c5;
209   SELECT * FROM c6;
210 } {{} aaa {} bbb i ccc}
212 do_execsql_test 3.3.0 {
213   CREATE TABLE r1(a UNIQUE, b UNIQUE);
214   INSERT INTO r1 VALUES('i', 'i');
215   INSERT INTO r1 VALUES('ii', 'ii');
216   INSERT INTO r1 VALUES('iii', 'iii');
217   INSERT INTO r1 VALUES('iv', 'iv');
218   INSERT INTO r1 VALUES('v', 'v');
219   INSERT INTO r1 VALUES('vi', 'vi');
220   INSERT INTO r1 VALUES('vii', 'vii');
223 do_changes_test 3.3.1 { INSERT OR REPLACE INTO r1 VALUES('i', 1)    }   1
224 do_changes_test 3.3.2 { INSERT OR REPLACE INTO r1 VALUES('iv', 'v') }   1
225 do_changes_test 3.3.3 { UPDATE OR REPLACE r1 SET b='v' WHERE a='iii' }  1
226 do_changes_test 3.3.4 { UPDATE OR REPLACE r1 SET b='vi',a='vii' WHERE a='ii' } 1
227 do_execsql_test 3.3.5 { 
228   SELECT * FROM r1 ORDER BY a;
229 } {i 1   iii v   vii vi}
232 #--------------------------------------------------------------------------
233 # EVIDENCE-OF: R-09813-48563 The value returned by sqlite3_changes()
234 # immediately after an INSERT, UPDATE or DELETE statement run on a view
235 # is always zero.
237 reset_db
238 do_execsql_test 4.1 {
239   CREATE TABLE log(log);
240   CREATE TABLE t1(x, y);
241   INSERT INTO t1 VALUES(1, 2);
242   INSERT INTO t1 VALUES(3, 4);
243   INSERT INTO t1 VALUES(5, 6);
245   CREATE VIEW v1 AS SELECT * FROM t1;
246   CREATE TRIGGER v1_i INSTEAD OF INSERT ON v1 BEGIN
247     INSERT INTO log VALUES('insert');
248   END;
249   CREATE TRIGGER v1_u INSTEAD OF UPDATE ON v1 BEGIN
250     INSERT INTO log VALUES('update'), ('update');
251   END;
252   CREATE TRIGGER v1_d INSTEAD OF DELETE ON v1 BEGIN
253     INSERT INTO log VALUES('delete'), ('delete'), ('delete');
254   END;
257 do_changes_test 4.2.1 { INSERT INTO t1 SELECT * FROM t1 }  3
258 do_changes_test 4.2.2 { INSERT INTO v1 VALUES(1, 2) }      0
260 do_changes_test 4.3.1 { INSERT INTO t1 SELECT * FROM t1 }  6
261 do_changes_test 4.3.2 { UPDATE v1 SET y='xyz' WHERE x=1 }  0
263 do_changes_test 4.4.1 { INSERT INTO t1 SELECT * FROM t1 } 12
264 do_changes_test 4.4.2 { DELETE FROM v1 WHERE x=5 }         0
267 #--------------------------------------------------------------------------
268 # EVIDENCE-OF: R-32918-61474 Before entering a trigger program the value
269 # returned by sqlite3_changes() function is saved. After the trigger
270 # program has finished, the original value is restored.
272 reset_db
273 db func my_changes my_changes
274 set ::changes [list]
275 proc my_changes {x} {
276   set res [db changes]
277   lappend ::changes $x $res
278   return $res
281 do_execsql_test 5.1.0 {
282   CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
283   CREATE TABLE t2(x);
284   INSERT INTO t1 VALUES(1, NULL);
285   INSERT INTO t1 VALUES(2, NULL);
286   INSERT INTO t1 VALUES(3, NULL);
287   CREATE TRIGGER AFTER UPDATE ON t1 BEGIN
288     INSERT INTO t2 VALUES('a'), ('b'), ('c');
289     SELECT my_changes('trigger');
290   END;
293 do_execsql_test 5.1.1 {
294   INSERT INTO t2 VALUES('a'), ('b');
295   UPDATE t1 SET b = my_changes('update');
296   SELECT * FROM t1;
297 } {1 2 2 2 3 2}
299 # Value is being restored to "2" when the trigger program exits.
300 do_test 5.1.2 {
301   set ::changes
302 } {update 2 trigger 3 update 2 trigger 3 update 2 trigger 3}
305 reset_db
306 do_execsql_test 5.2.0 {
307   CREATE TABLE t1(a, b);
308   CREATE TABLE log(x);
309   INSERT INTO t1 VALUES(1, 0);
310   INSERT INTO t1 VALUES(2, 0);
311   INSERT INTO t1 VALUES(3, 0);
312   CREATE TRIGGER t1_a_u AFTER UPDATE ON t1 BEGIN
313     INSERT INTO log VALUES(old.b || ' -> ' || new.b || ' c = ' || changes() );
314   END;
315   CREATE TABLE t2(a);
316   INSERT INTO t2 VALUES(1), (2), (3);
317   UPDATE t1 SET b = changes();
319 do_execsql_test 5.2.1 {
320   SELECT * FROM t1;
321 } {1 3 2 3 3 3}
322 do_execsql_test 5.2.2 {
323   SELECT * FROM log;
324 } {{0 -> 3 c = 3} {0 -> 3 c = 3} {0 -> 3 c = 3}}
327 #--------------------------------------------------------------------------
328 # EVIDENCE-OF: R-17146-37073 Within a trigger program each INSERT,
329 # UPDATE and DELETE statement sets the value returned by
330 # sqlite3_changes() upon completion as normal. Of course, this value
331 # will not include any changes performed by sub-triggers, as the
332 # sqlite3_changes() value will be saved and restored after each
333 # sub-trigger has run.
334 reset_db
335 do_execsql_test 6.0 {
337   CREATE TABLE t1(a, b);
338   CREATE TABLE t2(a, b);
339   CREATE TABLE t3(a, b);
340   CREATE TABLE log(x);
342   CREATE TRIGGER t1_i BEFORE INSERT ON t1 BEGIN
343     INSERT INTO t2 VALUES(new.a, new.b), (new.a, new.b);
344     INSERT INTO log VALUES('t2->' || changes());
345   END;
347   CREATE TRIGGER t2_i AFTER INSERT ON t2 BEGIN
348     INSERT INTO t3 VALUES(new.a, new.b), (new.a, new.b), (new.a, new.b);
349     INSERT INTO log VALUES('t3->' || changes());
350   END;
352   CREATE TRIGGER t1_u AFTER UPDATE ON t1 BEGIN
353     UPDATE t2 SET b=new.b WHERE a=old.a;
354     INSERT INTO log VALUES('t2->' || changes());
355   END;
357   CREATE TRIGGER t2_u BEFORE UPDATE ON t2 BEGIN
358     UPDATE t3 SET b=new.b WHERE a=old.a;
359     INSERT INTO log VALUES('t3->' || changes());
360   END;
362   CREATE TRIGGER t1_d AFTER DELETE ON t1 BEGIN
363     DELETE FROM t2 WHERE a=old.a AND b=old.b;
364     INSERT INTO log VALUES('t2->' || changes());
365   END;
367   CREATE TRIGGER t2_d BEFORE DELETE ON t2 BEGIN
368     DELETE FROM t3 WHERE a=old.a AND b=old.b;
369     INSERT INTO log VALUES('t3->' || changes());
370   END;
373 do_changes_test 6.1 {
374   INSERT INTO t1 VALUES('+', 'o');
375   SELECT * FROM log;
376 } {t3->3 t3->3 t2->2 1}
378 do_changes_test 6.2 {
379   DELETE FROM log;
380   UPDATE t1 SET b='*';
381   SELECT * FROM log;
382 } {t3->6 t3->6 t2->2 1}
384 do_changes_test 6.3 {
385   DELETE FROM log;
386   DELETE FROM t1;
387   SELECT * FROM log;
388 } {t3->6 t3->0 t2->2 1}
391 #--------------------------------------------------------------------------
392 # EVIDENCE-OF: R-43399-09409 This means that if the changes() SQL
393 # function (or similar) is used by the first INSERT, UPDATE or DELETE
394 # statement within a trigger, it returns the value as set when the
395 # calling statement began executing.
397 # EVIDENCE-OF: R-53215-27584 If it is used by the second or subsequent
398 # such statement within a trigger program, the value returned reflects
399 # the number of rows modified by the previous INSERT, UPDATE or DELETE
400 # statement within the same trigger.
402 reset_db
403 do_execsql_test 7.1 {
404   CREATE TABLE q1(t);
405   CREATE TABLE q2(u, v);
406   CREATE TABLE q3(w);
408   CREATE TRIGGER q2_insert BEFORE INSERT ON q2 BEGIN
410     /* changes() returns value from previous I/U/D in callers context */
411     INSERT INTO q1 VALUES('1:' || changes());
413     /* changes() returns value of previous I/U/D in this context */
414     INSERT INTO q3 VALUES(changes()), (2), (3);
415     INSERT INTO q1 VALUES('2:' || changes());
416     INSERT INTO q3 VALUES(changes() + 3), (changes()+4);
417     SELECT 'this does not affect things!';
418     INSERT INTO q1 VALUES('3:' || changes());
419     UPDATE q3 SET w = w+10 WHERE w%2;
420     INSERT INTO q1 VALUES('4:' || changes());
421     DELETE FROM q3;
422     INSERT INTO q1 VALUES('5:' || changes());
423   END;
426 do_execsql_test 7.2 {
427   INSERT INTO q2 VALUES('x', 'y');
428   SELECT * FROM q1;
429 } {
430   1:0   2:3   3:2   4:3   5:5
433 do_execsql_test 7.3 {
434   DELETE FROM q1;
435   INSERT INTO q2 VALUES('x', 'y');
436   SELECT * FROM q1;
437 } {
438   1:5   2:3   3:2   4:3   5:5
443 finish_test