During the development of my recent patch "unused/redundant foreign key code" [0], I had developed a few additional test cases to increase the coverage in ri_triggers.c. They are in the attached patches with explanations. With these, coverage should be pretty complete, except hard-to-trigger error cases. Interested reviewers can also follow along on coverage.postgresql.org.
[0]: https://www.postgresql.org/message-id/flat/2fb8d28c-a4e1-f206-898b-69cd22a39...@2ndquadrant.com/ -- Peter Eisentraut http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From 8eb6be9c2d458af9abf58d6c55929c54036cd9db Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Wed, 6 Jun 2018 22:30:16 -0400 Subject: [PATCH 1/4] Add test case for ON DELETE NO ACTION/RESTRICT This was previously not covered at all; function RI_FKey_restrict_del() was not exercised in the tests. --- src/test/regress/expected/foreign_key.out | 10 ++++++++-- src/test/regress/sql/foreign_key.sql | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 085c9aba11..5525dd75b9 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -1339,7 +1339,7 @@ DETAIL: Key (f1)=(1) is still referenced from table "defc". -- Test the difference between NO ACTION and RESTRICT -- create temp table pp (f1 int primary key); -create temp table cc (f1 int references pp on update no action); +create temp table cc (f1 int references pp on update no action on delete no action); insert into pp values(12); insert into pp values(11); update pp set f1=f1+1; @@ -1348,9 +1348,12 @@ update pp set f1=f1+1; update pp set f1=f1+1; -- fail ERROR: update or delete on table "pp" violates foreign key constraint "cc_f1_fkey" on table "cc" DETAIL: Key (f1)=(13) is still referenced from table "cc". +delete from pp where f1 = 13; -- fail +ERROR: update or delete on table "pp" violates foreign key constraint "cc_f1_fkey" on table "cc" +DETAIL: Key (f1)=(13) is still referenced from table "cc". drop table pp, cc; create temp table pp (f1 int primary key); -create temp table cc (f1 int references pp on update restrict); +create temp table cc (f1 int references pp on update restrict on delete restrict); insert into pp values(12); insert into pp values(11); update pp set f1=f1+1; @@ -1358,6 +1361,9 @@ insert into cc values(13); update pp set f1=f1+1; -- fail ERROR: update or delete on table "pp" violates foreign key constraint "cc_f1_fkey" on table "cc" DETAIL: Key (f1)=(13) is still referenced from table "cc". +delete from pp where f1 = 13; -- fail +ERROR: update or delete on table "pp" violates foreign key constraint "cc_f1_fkey" on table "cc" +DETAIL: Key (f1)=(13) is still referenced from table "cc". drop table pp, cc; -- -- Test interaction of foreign-key optimization with rules (bug #14219) diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 068ab2aab7..615588c318 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -992,22 +992,24 @@ CREATE TEMP TABLE tasks ( -- Test the difference between NO ACTION and RESTRICT -- create temp table pp (f1 int primary key); -create temp table cc (f1 int references pp on update no action); +create temp table cc (f1 int references pp on update no action on delete no action); insert into pp values(12); insert into pp values(11); update pp set f1=f1+1; insert into cc values(13); update pp set f1=f1+1; update pp set f1=f1+1; -- fail +delete from pp where f1 = 13; -- fail drop table pp, cc; create temp table pp (f1 int primary key); -create temp table cc (f1 int references pp on update restrict); +create temp table cc (f1 int references pp on update restrict on delete restrict); insert into pp values(12); insert into pp values(11); update pp set f1=f1+1; insert into cc values(13); update pp set f1=f1+1; -- fail +delete from pp where f1 = 13; -- fail drop table pp, cc; -- base-commit: ee2b37ae044f34851baba69e9ba737077326414e -- 2.19.2
From 01e43469973e9055da6f9fa16c95efac636b886a Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Sat, 16 Jun 2018 08:31:52 -0400 Subject: [PATCH 2/4] Increase test coverage in RI_FKey_pk_upd_check_required() This checks the case where the primary key has at least one null column. --- src/test/regress/expected/foreign_key.out | 24 +++++++++++++++++++++++ src/test/regress/sql/foreign_key.sql | 19 ++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 5525dd75b9..421ffbeae7 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -397,6 +397,30 @@ SELECT * from FKTABLE; | 3 | 4 | 5 (5 rows) +DROP TABLE FKTABLE; +DROP TABLE PKTABLE; +-- restrict with null values +CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, UNIQUE(ptest1, ptest2, ptest3) ); +CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 + FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE (ptest1, ptest2, ptest3)); +INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); +INSERT INTO PKTABLE VALUES (1, 3, NULL, 'test2'); +INSERT INTO PKTABLE VALUES (2, NULL, 4, 'test3'); +INSERT INTO FKTABLE VALUES (1, 2, 3, 1); +DELETE FROM PKTABLE WHERE ptest1 = 2; +SELECT * FROM PKTABLE; + ptest1 | ptest2 | ptest3 | ptest4 +--------+--------+--------+-------- + 1 | 2 | 3 | test1 + 1 | 3 | | test2 +(2 rows) + +SELECT * FROM FKTABLE; + ftest1 | ftest2 | ftest3 | ftest4 +--------+--------+--------+-------- + 1 | 2 | 3 | 1 +(1 row) + DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- cascade update/delete diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 615588c318..d3ed72b1fc 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -260,6 +260,25 @@ CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAI DROP TABLE FKTABLE; DROP TABLE PKTABLE; +-- restrict with null values +CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, UNIQUE(ptest1, ptest2, ptest3) ); +CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 + FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE (ptest1, ptest2, ptest3)); + +INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); +INSERT INTO PKTABLE VALUES (1, 3, NULL, 'test2'); +INSERT INTO PKTABLE VALUES (2, NULL, 4, 'test3'); + +INSERT INTO FKTABLE VALUES (1, 2, 3, 1); + +DELETE FROM PKTABLE WHERE ptest1 = 2; + +SELECT * FROM PKTABLE; +SELECT * FROM FKTABLE; + +DROP TABLE FKTABLE; +DROP TABLE PKTABLE; + -- cascade update/delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 -- 2.19.2
From 98e3a93d90ff21a18a95e759ac49b04b58c1cf70 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Wed, 21 Nov 2018 13:18:14 +0100 Subject: [PATCH 3/4] Increase test coverage in RI_FKey_fk_upd_check_required() This checks the code path of FKCONSTR_MATCH_FULL and RI_KEYS_SOME_NULL. --- src/test/regress/expected/foreign_key.out | 8 +++++++- src/test/regress/sql/foreign_key.sql | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 421ffbeae7..6b2df98c7e 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -143,6 +143,12 @@ SELECT * FROM FKTABLE; | | 8 (5 rows) +-- Check update with part of key null +UPDATE FKTABLE SET ftest1 = NULL WHERE ftest1 = 1; +ERROR: insert or update on table "fktable" violates foreign key constraint "constrname" +DETAIL: MATCH FULL does not allow mixing of null and nonnull key values. +-- Check update with old and new key values equal +UPDATE FKTABLE SET ftest1 = 1 WHERE ftest1 = 1; -- Try altering the column type where foreign keys are involved ALTER TABLE PKTABLE ALTER COLUMN ptest1 TYPE bigint; ALTER TABLE FKTABLE ALTER COLUMN ftest1 TYPE bigint; @@ -158,11 +164,11 @@ SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; ftest1 | ftest2 | ftest3 --------+--------+-------- - 1 | 3 | 5 3 | 6 | 12 | | 0 | | 4 | | 8 + 1 | 3 | 5 (5 rows) DROP TABLE PKTABLE CASCADE; diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index d3ed72b1fc..9dd5e29c75 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -97,6 +97,12 @@ CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, CONSTRAINT constrname -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; +-- Check update with part of key null +UPDATE FKTABLE SET ftest1 = NULL WHERE ftest1 = 1; + +-- Check update with old and new key values equal +UPDATE FKTABLE SET ftest1 = 1 WHERE ftest1 = 1; + -- Try altering the column type where foreign keys are involved ALTER TABLE PKTABLE ALTER COLUMN ptest1 TYPE bigint; ALTER TABLE FKTABLE ALTER COLUMN ftest1 TYPE bigint; -- 2.19.2
From 2b5ab918e7706639b960d9a053dfc97176df4546 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Sun, 25 Nov 2018 16:27:37 +0100 Subject: [PATCH 4/4] Increase test coverage in RI_Initial_Check() This covers the special error handling of FKCONSTR_MATCH_FULL. --- src/test/regress/expected/foreign_key.out | 12 ++++++++++++ src/test/regress/sql/foreign_key.sql | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 6b2df98c7e..1241aab6e3 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -339,6 +339,18 @@ SELECT * FROM PKTABLE; 0 | Test4 (4 rows) +DROP TABLE FKTABLE; +DROP TABLE PKTABLE; +-- +-- Check initial check upon ALTER TABLE +-- +CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, PRIMARY KEY(ptest1, ptest2) ); +CREATE TABLE FKTABLE ( ftest1 int, ftest2 int ); +INSERT INTO PKTABLE VALUES (1, 2); +INSERT INTO FKTABLE VALUES (1, NULL); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) REFERENCES PKTABLE MATCH FULL; +ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey" +DETAIL: MATCH FULL does not allow mixing of null and nonnull key values. DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- MATCH SIMPLE diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 9dd5e29c75..9ff2611377 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -219,6 +219,20 @@ CREATE TABLE FKTABLE ( ftest1 int REFERENCES PKTABLE MATCH FULL, ftest2 int ); DROP TABLE FKTABLE; DROP TABLE PKTABLE; +-- +-- Check initial check upon ALTER TABLE +-- +CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, PRIMARY KEY(ptest1, ptest2) ); +CREATE TABLE FKTABLE ( ftest1 int, ftest2 int ); + +INSERT INTO PKTABLE VALUES (1, 2); +INSERT INTO FKTABLE VALUES (1, NULL); + +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) REFERENCES PKTABLE MATCH FULL; + +DROP TABLE FKTABLE; +DROP TABLE PKTABLE; + -- MATCH SIMPLE -- 2.19.2