From: dan Date: Fri, 9 Oct 2009 18:16:55 +0000 (+0000) Subject: Add test cases to e_fkey.test. X-Git-Tag: fts3-refactor~114 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2140429ccdb1abec45e66e961c3cbdf79cbe7eb0;p=thirdparty%2Fsqlite.git Add test cases to e_fkey.test. FossilOrigin-Name: e391dfe79d52e1de48688715d4a6707ffca214fd --- diff --git a/manifest b/manifest index 31fbfd69e6..5ee1d26795 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Test\sa\scouple\sof\sthe\sexamples\sin\sforeignkeys.html. -D 2009-10-08T19:02:51 +C Add\stest\scases\sto\se_fkey.test. +D 2009-10-09T18:16:55 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -319,7 +319,7 @@ F test/descidx2.test 1310ed1326cdfed4ea2c55169631579f082d174f F test/descidx3.test 3394ad4d089335cac743c36a14129d6d931c316f F test/diskfull.test 0cede7ef9d8f415d9d3944005c76be7589bb5ebb F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376 -F test/e_fkey.test 9ad5b376c95dfa60281b5931e6a99d446387446c +F test/e_fkey.test 5106b8fb0832c9b6d90541c98577494b3057254f F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398 F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041 @@ -756,7 +756,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 23e0f61a4f24315bf31f632f43b60ec232f348fb -R bf6536ce577073a4ab6551eb95f10373 +P a2fb7902c01f8b095b25d338fc2f23cb26c941d9 +R 915c62ba9334f3e6b47b204d14e080a3 U dan -Z baa870c437a97e37a52b46ca7fd8e861 +Z 7b2c0c612ba82ec335a32afc5fdab39c diff --git a/manifest.uuid b/manifest.uuid index 77e45fb82a..29a7922bf4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a2fb7902c01f8b095b25d338fc2f23cb26c941d9 \ No newline at end of file +e391dfe79d52e1de48688715d4a6707ffca214fd \ No newline at end of file diff --git a/test/e_fkey.test b/test/e_fkey.test index cfa5fd4dce..cb4888bb07 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -19,39 +19,581 @@ ifcapable {!foreignkey} { finish_test ; return } execsql "PRAGMA foreign_keys = ON" +########################################################################### +### SECTION 4.3: ON DELETE and ON UPDATE Actions +########################################################################### + #------------------------------------------------------------------------- -# /* EV: R-51437-39891 */ +# /* EV: R-48270-44282 */ +# +# Test that configured ON DELETE and ON UPDATE actions take place when +# deleting or modifying rows of the parent table, respectively. +# +# /* EV: R-48124-63225 */ +# +# Test that a single FK constraint may have different actions configured +# for ON DELETE and ON UPDATE. +# +do_test e_fkey-16.1 { + execsql { + CREATE TABLE p(a, b PRIMARY KEY, c); + CREATE TABLE c1(d, e, f DEFAULT 'k0' REFERENCES p + ON UPDATE SET DEFAULT + ON DELETE SET NULL + ); + + INSERT INTO p VALUES(0, 'k0', ''); + INSERT INTO p VALUES(1, 'k1', 'I'); + INSERT INTO p VALUES(2, 'k2', 'II'); + INSERT INTO p VALUES(3, 'k3', 'III'); + + INSERT INTO c1 VALUES(1, 'xx', 'k1'); + INSERT INTO c1 VALUES(2, 'xx', 'k2'); + INSERT INTO c1 VALUES(3, 'xx', 'k3'); + } +} {} +do_test e_fkey-16.2 { + execsql { + UPDATE p SET b = 'k4' WHERE a = 1; + SELECT * FROM c1; + } +} {1 xx k0 2 xx k2 3 xx k3} +do_test e_fkey-16.3 { + execsql { + DELETE FROM p WHERE a = 2; + SELECT * FROM c1; + } +} {1 xx k0 2 xx {} 3 xx k3} +do_test e_fkey-16.4 { + execsql { + CREATE UNIQUE INDEX pi ON p(c); + REPLACE INTO p VALUES(5, 'k5', 'III'); + SELECT * FROM c1; + } +} {1 xx k0 2 xx {} 3 xx {}} + +#------------------------------------------------------------------------- +# /* EV: R-33326-45252 */ +# +# Each foreign key in the system has an ON UPDATE and ON DELETE action, +# either "NO ACTION", "RESTRICT", "SET NULL", "SET DEFAULT" or "CASCADE". +# +# /* EV: R-19803-45884 */ +# +# If none is specified explicitly, "NO ACTION" is the default. +# +drop_all_tables +do_test e_fkey-17.1 { + execsql { + CREATE TABLE parent(x PRIMARY KEY, y); + CREATE TABLE child1(a, + b REFERENCES parent ON UPDATE NO ACTION ON DELETE RESTRICT + ); + CREATE TABLE child2(a, + b REFERENCES parent ON UPDATE RESTRICT ON DELETE SET NULL + ); + CREATE TABLE child3(a, + b REFERENCES parent ON UPDATE SET NULL ON DELETE SET DEFAULT + ); + CREATE TABLE child4(a, + b REFERENCES parent ON UPDATE SET DEFAULT ON DELETE CASCADE + ); + + -- Create some foreign keys that use the default action - "NO ACTION" + CREATE TABLE child5(a, b REFERENCES parent ON UPDATE CASCADE); + CREATE TABLE child6(a, b REFERENCES parent ON DELETE RESTRICT); + CREATE TABLE child7(a, b REFERENCES parent ON DELETE NO ACTION); + CREATE TABLE child8(a, b REFERENCES parent ON UPDATE NO ACTION); + } +} {} + +foreach {tn zTab lRes} { + 2 child1 {0 0 parent b {} {NO ACTION} RESTRICT NONE} + 3 child2 {0 0 parent b {} RESTRICT {SET NULL} NONE} + 4 child3 {0 0 parent b {} {SET NULL} {SET DEFAULT} NONE} + 5 child4 {0 0 parent b {} {SET DEFAULT} CASCADE NONE} + 6 child5 {0 0 parent b {} CASCADE {NO ACTION} NONE} + 7 child6 {0 0 parent b {} {NO ACTION} RESTRICT NONE} + 8 child7 {0 0 parent b {} {NO ACTION} {NO ACTION} NONE} + 9 child8 {0 0 parent b {} {NO ACTION} {NO ACTION} NONE} +} { + do_test e_fkey-17.$tn { execsql "PRAGMA foreign_key_list($zTab)" } $lRes +} + +#------------------------------------------------------------------------- +# /* EV: R-19971-54976 */ +# +# Test that "NO ACTION" means that nothing happens to a child row when +# it's parent row is updated or deleted. +# +drop_all_tables +do_test e_fkey-18.1 { + execsql { + CREATE TABLE parent(p1, p2, PRIMARY KEY(p1, p2)); + CREATE TABLE child(c1, c2, + FOREIGN KEY(c1, c2) REFERENCES parent + ON UPDATE NO ACTION + ON DELETE NO ACTION + DEFERRABLE INITIALLY DEFERRED + ); + INSERT INTO parent VALUES('j', 'k'); + INSERT INTO parent VALUES('l', 'm'); + INSERT INTO child VALUES('j', 'k'); + INSERT INTO child VALUES('l', 'm'); + } +} {} +do_test e_fkey-18.2 { + execsql { + BEGIN; + UPDATE parent SET p1='k' WHERE p1='j'; + DELETE FROM parent WHERE p1='l'; + SELECT * FROM child; + } +} {j k l m} +do_test e_fkey-18.3 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-18.4 { + execsql ROLLBACK +} {} + +#------------------------------------------------------------------------- +# /* EV: R-04272-38653 */ +# +# Test that "RESTRICT" means the application is prohibited from deleting +# or updating a parent table row when there exists one or more child keys +# mapped to it. +# +drop_all_tables +do_test e_fkey-18.1 { + execsql { + CREATE TABLE parent(p1, p2); + CREATE UNIQUE INDEX parent_i ON parent(p1, p2); + CREATE TABLE child1(c1, c2, + FOREIGN KEY(c2, c1) REFERENCES parent(p1, p2) ON DELETE RESTRICT + ); + CREATE TABLE child2(c1, c2, + FOREIGN KEY(c2, c1) REFERENCES parent(p1, p2) ON UPDATE RESTRICT + ); + } +} {} +do_test e_fkey-18.2 { + execsql { + INSERT INTO parent VALUES('a', 'b'); + INSERT INTO parent VALUES('c', 'd'); + INSERT INTO child1 VALUES('b', 'a'); + INSERT INTO child2 VALUES('d', 'c'); + } +} {} +do_test e_fkey-18.3 { + catchsql { DELETE FROM parent WHERE p1 = 'a' } +} {1 {foreign key constraint failed}} +do_test e_fkey-18.4 { + catchsql { UPDATE parent SET p2 = 'e' WHERE p1 = 'c' } +} {1 {foreign key constraint failed}} + +#------------------------------------------------------------------------- +# /* EV: R-37997-42187 */ +# +# Test that RESTRICT is slightly different from NO ACTION for IMMEDIATE +# constraints, in that it is enforced immediately, not at the end of the +# statement. +# +drop_all_tables +do_test e_fkey-19.1 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON UPDATE RESTRICT); + CREATE TABLE child2(c REFERENCES parent ON UPDATE NO ACTION); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + + CREATE TRIGGER parent_t AFTER UPDATE ON parent BEGIN + UPDATE child1 set c = new.x WHERE c = old.x; + UPDATE child2 set c = new.x WHERE c = old.x; + END; + } +} {} +do_test e_fkey-19.2 { + catchsql { UPDATE parent SET x = 'key one' WHERE x = 'key1' } +} {1 {foreign key constraint failed}} +do_test e_fkey-19.3 { + execsql { + UPDATE parent SET x = 'key two' WHERE x = 'key2'; + SELECT * FROM child2; + } +} {{key two}} + +drop_all_tables +do_test e_fkey-19.4 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON DELETE RESTRICT); + CREATE TABLE child2(c REFERENCES parent ON DELETE NO ACTION); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + + CREATE TRIGGER parent_t AFTER DELETE ON parent BEGIN + UPDATE child1 SET c = NULL WHERE c = old.x; + UPDATE child2 SET c = NULL WHERE c = old.x; + END; + } +} {} +do_test e_fkey-19.5 { + catchsql { DELETE FROM parent WHERE x = 'key1' } +} {1 {foreign key constraint failed}} +do_test e_fkey-19.6 { + execsql { + DELETE FROM parent WHERE x = 'key2'; + SELECT * FROM child2; + } +} {{}} + +drop_all_tables +do_test e_fkey-19.7 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON DELETE RESTRICT); + CREATE TABLE child2(c REFERENCES parent ON DELETE NO ACTION); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + } +} {} +do_test e_fkey-19.8 { + catchsql { REPLACE INTO parent VALUES('key1') } +} {1 {foreign key constraint failed}} +do_test e_fkey-19.9 { + execsql { + REPLACE INTO parent VALUES('key2'); + SELECT * FROM child2; + } +} {key2} + +#------------------------------------------------------------------------- +# /* EV: R-24179-60523 */ +# +# Test that RESTRICT is enforced immediately, even for a DEFERRED constraint. +# +drop_all_tables +do_test e_fkey-20.1 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON UPDATE RESTRICT + DEFERRABLE INITIALLY DEFERRED + ); + CREATE TABLE child2(c REFERENCES parent ON UPDATE NO ACTION + DEFERRABLE INITIALLY DEFERRED + ); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + BEGIN; + } +} {} +do_test e_fkey-20.2 { + catchsql { UPDATE parent SET x = 'key one' WHERE x = 'key1' } +} {1 {foreign key constraint failed}} +do_test e_fkey-20.3 { + execsql { UPDATE parent SET x = 'key two' WHERE x = 'key2' } +} {} +do_test e_fkey-20.4 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-20.5 { + execsql { + UPDATE child2 SET c = 'key two'; + COMMIT; + } +} {} + +drop_all_tables +do_test e_fkey-20.6 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON DELETE RESTRICT + DEFERRABLE INITIALLY DEFERRED + ); + CREATE TABLE child2(c REFERENCES parent ON DELETE NO ACTION + DEFERRABLE INITIALLY DEFERRED + ); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + BEGIN; + } +} {} +do_test e_fkey-20.7 { + catchsql { DELETE FROM parent WHERE x = 'key1' } +} {1 {foreign key constraint failed}} +do_test e_fkey-20.8 { + execsql { DELETE FROM parent WHERE x = 'key2' } +} {} +do_test e_fkey-20.9 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-20.10 { + execsql { + UPDATE child2 SET c = NULL; + COMMIT; + } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-03353-05327 */ +# +# Test SET NULL actions. +# +drop_all_tables +do_test e_fkey-21.1 { + execsql { + CREATE TABLE pA(x PRIMARY KEY); + CREATE TABLE cA(c REFERENCES pA ON DELETE SET NULL); + CREATE TABLE cB(c REFERENCES pA ON UPDATE SET NULL); + + INSERT INTO pA VALUES(X'ABCD'); + INSERT INTO pA VALUES(X'1234'); + INSERT INTO cA VALUES(X'ABCD'); + INSERT INTO cB VALUES(X'1234'); + } +} {} +do_test e_fkey-21.2 { + execsql { + DELETE FROM pA WHERE rowid = 1; + SELECT quote(x) FROM pA; + } +} {X'1234'} +do_test e_fkey-21.3 { + execsql { + SELECT quote(c) FROM cA; + } +} {NULL} +do_test e_fkey-21.4 { + execsql { + UPDATE pA SET x = X'8765' WHERE rowid = 2; + SELECT quote(x) FROM pA; + } +} {X'8765'} +do_test e_fkey-21.5 { + execsql { SELECT quote(c) FROM cB } +} {NULL} + +#------------------------------------------------------------------------- +# /* EV: R-43054-54832 */ +# +# Test SET DEFAULT actions. +# +drop_all_tables +do_test e_fkey-22.1 { + execsql { + CREATE TABLE pA(x PRIMARY KEY); + CREATE TABLE cA(c DEFAULT X'0000' REFERENCES pA ON DELETE SET DEFAULT); + CREATE TABLE cB(c DEFAULT X'9999' REFERENCES pA ON UPDATE SET DEFAULT); + + INSERT INTO pA(rowid, x) VALUES(1, X'0000'); + INSERT INTO pA(rowid, x) VALUES(2, X'9999'); + INSERT INTO pA(rowid, x) VALUES(3, X'ABCD'); + INSERT INTO pA(rowid, x) VALUES(4, X'1234'); + + INSERT INTO cA VALUES(X'ABCD'); + INSERT INTO cB VALUES(X'1234'); + } +} {} +do_test e_fkey-22.2 { + execsql { + DELETE FROM pA WHERE rowid = 3; + SELECT quote(x) FROM pA; + } +} {X'0000' X'9999' X'1234'} +do_test e_fkey-22.3 { + execsql { SELECT quote(c) FROM cA } +} {X'0000'} +do_test e_fkey-22.4 { + execsql { + UPDATE pA SET x = X'8765' WHERE rowid = 4; + SELECT quote(x) FROM pA; + } +} {X'0000' X'9999' X'8765'} +do_test e_fkey-22.5 { + execsql { SELECT quote(c) FROM cB } +} {X'9999'} + +#------------------------------------------------------------------------- +# /* EV: R-61376-57267 */ +# /* EV: R-61809-62207 */ +# +# Test ON DELETE CASCADE actions. +# +drop_all_tables +do_test e_fkey-23.1 { + execsql { + CREATE TABLE p1(a, b UNIQUE); + CREATE TABLE c1(c REFERENCES p1(b) ON DELETE CASCADE, d); + INSERT INTO p1 VALUES(NULL, NULL); + INSERT INTO p1 VALUES(4, 4); + INSERT INTO p1 VALUES(5, 5); + INSERT INTO c1 VALUES(NULL, NULL); + INSERT INTO c1 VALUES(4, 4); + INSERT INTO c1 VALUES(5, 5); + SELECT count(*) FROM c1; + } +} {3} +do_test e_fkey-23.2 { + execsql { + DELETE FROM p1 WHERE a = 4; + SELECT d, c FROM c1; + } +} {{} {} 5 5} +do_test e_fkey-23.3 { + execsql { + DELETE FROM p1; + SELECT d, c FROM c1; + } +} {{} {}} +do_test e_fkey-23.4 { + execsql { SELECT * FROM p1 } +} {} + + +#------------------------------------------------------------------------- +# /* EV: R-61376-57267 */ +# /* EV: R-13877-64542 */ +# +# Test ON UPDATE CASCADE actions. +# +drop_all_tables +do_test e_fkey-24.1 { + execsql { + CREATE TABLE p1(a, b UNIQUE); + CREATE TABLE c1(c REFERENCES p1(b) ON UPDATE CASCADE, d); + INSERT INTO p1 VALUES(NULL, NULL); + INSERT INTO p1 VALUES(4, 4); + INSERT INTO p1 VALUES(5, 5); + INSERT INTO c1 VALUES(NULL, NULL); + INSERT INTO c1 VALUES(4, 4); + INSERT INTO c1 VALUES(5, 5); + SELECT count(*) FROM c1; + } +} {3} +do_test e_fkey-24.2 { + execsql { + UPDATE p1 SET b = 10 WHERE b = 5; + SELECT d, c FROM c1; + } +} {{} {} 4 4 5 10} +do_test e_fkey-24.3 { + execsql { + UPDATE p1 SET b = 11 WHERE b = 4; + SELECT d, c FROM c1; + } +} {{} {} 4 11 5 10} +do_test e_fkey-24.4 { + execsql { + UPDATE p1 SET b = 6 WHERE b IS NULL; + SELECT d, c FROM c1; + } +} {{} {} 4 11 5 10} +do_test e_fkey-23.5 { + execsql { SELECT * FROM p1 } +} {{} 6 4 11 5 10} + +#------------------------------------------------------------------------- +# /* EV: R-51329-33438 */ # # Test an example from the "ON DELETE and ON UPDATE Actions" section # of foreignkeys.html. # drop_all_tables -do_test e_fkey-13.1 { +do_test e_fkey-15.1 { execsql { - CREATE TABLE parent(x PRIMARY KEY); - CREATE TABLE child(y REFERENCES parent ON UPDATE SET NULL); - INSERT INTO parent VALUES('key'); - INSERT INTO child VALUES('key'); + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER REFERENCES artist(artistid) ON UPDATE CASCADE + ); + + INSERT INTO artist VALUES(1, 'Dean Martin'); + INSERT INTO artist VALUES(2, 'Frank Sinatra'); + INSERT INTO track VALUES(11, 'That''s Amore', 1); + INSERT INTO track VALUES(12, 'Christmas Blues', 1); + INSERT INTO track VALUES(13, 'My Way', 2); } } {} -do_test e_fkey-13.2 { +do_test e_fkey-15.2 { execsql { - UPDATE parent SET x = 'key'; - SELECT IFNULL(y, 'null') FROM child; + UPDATE artist SET artistid = 100 WHERE artistname = 'Dean Martin'; } -} {key} -do_test e_fkey-13.3 { +} {} +do_test e_fkey-15.3 { + execsql { SELECT * FROM artist } +} {2 {Frank Sinatra} 100 {Dean Martin}} +do_test e_fkey-15.4 { + execsql { SELECT * FROM track } +} {11 {That's Amore} 100 12 {Christmas Blues} 100 13 {My Way} 2} + + +#------------------------------------------------------------------------- +# /* EV: R-53968-51642 */ +# +# Verify that adding an FK action does not absolve the user of the +# requirement not to violate the foreign key constraint. +# +drop_all_tables +do_test e_fkey-25.1 { execsql { - UPDATE parent SET x = 'key2'; - SELECT IFNULL(y, 'null') FROM child; + CREATE TABLE parent(a COLLATE nocase, b, c, PRIMARY KEY(c, a)); + CREATE TABLE child(d DEFAULT 'a', e, f DEFAULT 'c', + FOREIGN KEY(f, d) REFERENCES parent ON UPDATE SET DEFAULT + ); + + INSERT INTO parent VALUES('A', 'b', 'c'); + INSERT INTO parent VALUES('ONE', 'two', 'three'); + INSERT INTO child VALUES('one', 'two', 'three'); } -} {null} +} {} +do_test e_fkey-25.2 { + execsql { + BEGIN; + UPDATE parent SET a = '' WHERE a = 'oNe'; + SELECT * FROM child; + } +} {a two c} +do_test e_fkey-25.3 { + execsql { + ROLLBACK; + DELETE FROM parent WHERE a = 'A'; + SELECT * FROM parent; + } +} {ONE two three} +do_test e_fkey-25.4 { + catchsql { UPDATE parent SET a = '' WHERE a = 'oNe' } +} {1 {foreign key constraint failed}} + #------------------------------------------------------------------------- # /* EV: R-07065-59588 */ +# /* EV: R-28220-46694 */ # # Test an example from the "ON DELETE and ON UPDATE Actions" section -# of foreignkeys.html. +# of foreignkeys.html. This example shows that adding an "ON DELETE DEFAULT" +# clause does not abrogate the need to satisfy the foreign key constraint +# (R-28220-46694). # drop_all_tables do_test e_fkey-14.1 { @@ -86,42 +628,144 @@ do_test e_fkey-14.5 { } {14 {Mr. Bojangles} 0} #------------------------------------------------------------------------- -# /* EV: R-25213-27898 */ +# /* EV: R-35762-30306 */ # -# Test an example from the "ON DELETE and ON UPDATE Actions" section -# of foreignkeys.html. +# Check that the order of steps in an UPDATE or DELETE on a parent +# table is as follows: +# +# 1. Execute applicable BEFORE trigger programs, +# 2. Check local (non foreign key) constraints, +# 3. Update or delete the row in the parent table, +# 4. Perform any required foreign key actions, +# 5. Execute applicable AFTER trigger programs. # drop_all_tables -do_test e_fkey-15.1 { +do_test e_fkey-27.1 { + proc maxparent {args} { db one {SELECT max(x) FROM parent} } + db func maxparent maxparent + execsql { - CREATE TABLE artist( - artistid INTEGER PRIMARY KEY, - artistname TEXT - ); - CREATE TABLE track( - trackid INTEGER, - trackname TEXT, - trackartist INTEGER REFERENCES artist(artistid) ON UPDATE CASCADE + CREATE TABLE parent(x PRIMARY KEY); + + CREATE TRIGGER bu BEFORE UPDATE ON parent BEGIN + INSERT INTO parent VALUES(new.x-old.x); + END; + CREATE TABLE child( + a DEFAULT (maxparent()) REFERENCES parent ON UPDATE SET DEFAULT ); + CREATE TRIGGER au AFTER UPDATE ON parent BEGIN + INSERT INTO parent VALUES(new.x+old.x); + END; - INSERT INTO artist VALUES(1, 'Dean Martin'); - INSERT INTO artist VALUES(2, 'Frank Sinatra'); - INSERT INTO track VALUES(11, 'That''s Amore', 1); - INSERT INTO track VALUES(12, 'Christmas Blues', 1); - INSERT INTO track VALUES(13, 'My Way', 2); + INSERT INTO parent VALUES(1); + INSERT INTO child VALUES(1); } } {} -do_test e_fkey-15.2 { +do_test e_fkey-27.2 { execsql { - UPDATE artist SET artistid = 100 WHERE artistname = 'Dean Martin'; + UPDATE parent SET x = 22; + SELECT * FROM parent UNION ALL SELECT 'xxx' UNION ALL SELECT a FROM child; + } +} {22 21 23 xxx 22} +do_test e_fkey-27.3 { + execsql { + DELETE FROM child; + DELETE FROM parent; + INSERT INTO parent VALUES(-1); + INSERT INTO child VALUES(-1); + UPDATE parent SET x = 22; + SELECT * FROM parent UNION ALL SELECT 'xxx' UNION ALL SELECT a FROM child; + } +} {22 23 21 xxx 23} + + +#------------------------------------------------------------------------- +# /* EV: R-27383-10246 */ +# +# Verify that ON UPDATE actions only actually take place if the parent key +# is set to a new value that is distinct from the old value. The default +# collation sequence and affinity are used to determine if the new value +# is 'distinct' from the old or not. +# +drop_all_tables +do_test e_fkey-26.1 { + execsql { + CREATE TABLE zeus(a INTEGER COLLATE NOCASE, b, PRIMARY KEY(a, b)); + CREATE TABLE apollo(c, d, + FOREIGN KEY(c, d) REFERENCES zeus ON UPDATE CASCADE + ); + INSERT INTO zeus VALUES('abc', 'xyz'); + INSERT INTO apollo VALUES('ABC', 'xyz'); + } + execsql { + UPDATE zeus SET a = 'aBc'; + SELECT * FROM apollo; + } +} {ABC xyz} +do_test e_fkey-26.2 { + execsql { + UPDATE zeus SET a = 1, b = 1; + SELECT * FROM apollo; + } +} {1 1} +do_test e_fkey-26.3 { + execsql { + UPDATE zeus SET a = 1, b = 1; + SELECT typeof(c), c, typeof(d), d FROM apollo; + } +} {integer 1 integer 1} +do_test e_fkey-26.4 { + execsql { + UPDATE zeus SET a = '1'; + SELECT typeof(c), c, typeof(d), d FROM apollo; + } +} {integer 1 integer 1} +do_test e_fkey-26.5 { + execsql { + UPDATE zeus SET b = '1'; + SELECT typeof(c), c, typeof(d), d FROM apollo; + } +} {integer 1 text 1} +do_test e_fkey-26.6 { + execsql { + UPDATE zeus SET b = NULL; + SELECT typeof(c), c, typeof(d), d FROM apollo; + } +} {integer 1 null {}} + +#------------------------------------------------------------------------- +# /* EV: R-51437-39891 */ +# +# Test an example from the "ON DELETE and ON UPDATE Actions" section +# of foreignkeys.html. This example demonstrates that ON UPDATE actions +# only take place if at least one parent key column is set to a value +# that is distinct from its previous value. +# +drop_all_tables +do_test e_fkey-13.1 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child(y REFERENCES parent ON UPDATE SET NULL); + INSERT INTO parent VALUES('key'); + INSERT INTO child VALUES('key'); } } {} -do_test e_fkey-15.3 { - execsql { SELECT * FROM artist } -} {2 {Frank Sinatra} 100 {Dean Martin}} -do_test e_fkey-15.4 { - execsql { SELECT * FROM track } -} {11 {That's Amore} 100 12 {Christmas Blues} 100 13 {My Way} 2} +do_test e_fkey-13.2 { + execsql { + UPDATE parent SET x = 'key'; + SELECT IFNULL(y, 'null') FROM child; + } +} {key} +do_test e_fkey-13.3 { + execsql { + UPDATE parent SET x = 'key2'; + SELECT IFNULL(y, 'null') FROM child; + } +} {null} + +########################################################################### +### SECTION 5: CREATE, ALTER and DROP TABLE commands +########################################################################### #------------------------------------------------------------------------- # /* EV: R-36018-21755 */ @@ -478,6 +1122,9 @@ do_test e_fkey-12.3.3 { execsql { PRAGMA foreign_keys = ON } } {} +########################################################################### +### SECTION 6: Limits and Unsupported Features +########################################################################### #------------------------------------------------------------------------- # /* EV: R-24728-13230 */