diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 2993ba43e3..55d5d8b56a 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1230,7 +1230,38 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate, /* * Row movement, part 1. Delete the tuple, but skip RETURNING processing. * We want to return rows from INSERT. + * + * However, if the source partition is being referenced in a foreign key + * constraint inherited from some ancestor, then proceeding with the + * delete may result violating the constraint. */ + if (resultRelInfo->ri_TrigDesc != NULL && + resultRelInfo->ri_TrigDesc->trig_delete_after_row) + { + TriggerDesc *trigdesc = resultRelInfo->ri_TrigDesc; + int i; + bool found = false; + + for (i = 0; i < trigdesc->numtriggers; i++) + { + Trigger *trigger = &trigdesc->triggers[i]; + + if (trigger->tgisinternal && + OidIsValid(trigger->tgconstrrelid) && + RI_FKey_trigger_type(trigger->tgfoid) == RI_TRIGGER_PK) + { + found = true; + break; + } + } + + if (found) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move row being updated to another partition"), + errdetail("Moving the row may cause a foreign key involving the source partition to be violated."))); + } + ExecDelete(mtstate, resultRelInfo, tupleid, oldtuple, planSlot, epqstate, estate, false, /* processReturning */ diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 07bd5b6434..7f018936b9 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -2380,10 +2380,10 @@ DELETE FROM pk WHERE a = 20 RETURNING *; 20 (1 row) -UPDATE pk SET a = 90 WHERE a = 30 RETURNING *; +UPDATE pk SET a = 40 WHERE a = 30 RETURNING *; a ---- - 90 + 40 (1 row) SELECT tableoid::regclass, * FROM fk; @@ -2406,7 +2406,7 @@ INSERT INTO fk VALUES (20), (30); DELETE FROM pk WHERE a = 20; ERROR: update or delete on table "pk11" violates foreign key constraint "fk_a_fkey2" on table "fk" DETAIL: Key (a)=(20) is still referenced from table "fk". -UPDATE pk SET a = 90 WHERE a = 30; +UPDATE pk SET a = 40 WHERE a = 30; ERROR: update or delete on table "pk11" violates foreign key constraint "fk_a_fkey2" on table "fk" DETAIL: Key (a)=(30) is still referenced from table "fk". SELECT tableoid::regclass, * FROM fk; @@ -2416,6 +2416,11 @@ SELECT tableoid::regclass, * FROM fk; fk12 | 30 (2 rows) +-- Limitation: an update of pk causing a row to move between partitions is not +-- supported in the presence of a foreign key pointing to it +UPDATE pk SET a = 90 WHERE a = 30; +ERROR: cannot move row being updated to another partition +DETAIL: Moving the row may cause a foreign key involving the source partition to be violated. DROP TABLE fk; -- test for reported bug: relispartition not set -- https://postgr.es/m/CA+HiwqHMsRtRYRWYTWavKJ8x14AFsv7bmAV46mYwnfD3vy8goQ@mail.gmail.com diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index c5c9011afc..bc9afe66ae 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -1683,7 +1683,7 @@ ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE SET DEFAULT ON DELETE CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO fk VALUES (1, 20), (2, 30); DELETE FROM pk WHERE a = 20 RETURNING *; -UPDATE pk SET a = 90 WHERE a = 30 RETURNING *; +UPDATE pk SET a = 40 WHERE a = 30 RETURNING *; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; @@ -1697,8 +1697,13 @@ ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE RESTRICT ON DELETE RE CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO fk VALUES (20), (30); DELETE FROM pk WHERE a = 20; -UPDATE pk SET a = 90 WHERE a = 30; +UPDATE pk SET a = 40 WHERE a = 30; SELECT tableoid::regclass, * FROM fk; + +-- Limitation: an update of pk causing a row to move between partitions is not +-- supported in the presence of a foreign key pointing to it +UPDATE pk SET a = 90 WHERE a = 30; + DROP TABLE fk; -- test for reported bug: relispartition not set