diff options
| -rw-r--r-- | src/backend/executor/execMain.c | 24 | ||||
| -rw-r--r-- | src/test/regress/expected/foreign_key.out | 51 | ||||
| -rw-r--r-- | src/test/regress/sql/foreign_key.sql | 48 |
3 files changed, 116 insertions, 7 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 713e926329c..27c9eec697b 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1326,10 +1326,9 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, * Get a ResultRelInfo for a trigger target relation. * * Most of the time, triggers are fired on one of the result relations of the - * query, and so we can just return a member of the es_result_relations array, - * or the es_tuple_routing_result_relations list (if any). (Note: in self-join - * situations there might be multiple members with the same OID; if so it - * doesn't matter which one we pick.) + * query, and so we can just return a suitable one we already made and stored + * in the es_opened_result_relations or es_tuple_routing_result_relations + * Lists. * * However, it is sometimes necessary to fire triggers on other relations; * this happens mainly when an RI update trigger queues additional triggers @@ -1349,11 +1348,20 @@ ExecGetTriggerResultRel(EState *estate, Oid relid, Relation rel; MemoryContext oldcontext; + /* + * Before creating a new ResultRelInfo, check if we've already made and + * cached one for this relation. We must ensure that the given + * 'rootRelInfo' matches the one stored in the cached ResultRelInfo as + * trigger handling for partitions can result in mixed requirements for + * what ri_RootResultRelInfo is set to. + */ + /* Search through the query result relations */ foreach(l, estate->es_opened_result_relations) { rInfo = lfirst(l); - if (RelationGetRelid(rInfo->ri_RelationDesc) == relid) + if (RelationGetRelid(rInfo->ri_RelationDesc) == relid && + rInfo->ri_RootResultRelInfo == rootRelInfo) return rInfo; } @@ -1364,7 +1372,8 @@ ExecGetTriggerResultRel(EState *estate, Oid relid, foreach(l, estate->es_tuple_routing_result_relations) { rInfo = (ResultRelInfo *) lfirst(l); - if (RelationGetRelid(rInfo->ri_RelationDesc) == relid) + if (RelationGetRelid(rInfo->ri_RelationDesc) == relid && + rInfo->ri_RootResultRelInfo == rootRelInfo) return rInfo; } @@ -1372,7 +1381,8 @@ ExecGetTriggerResultRel(EState *estate, Oid relid, foreach(l, estate->es_trig_target_relations) { rInfo = (ResultRelInfo *) lfirst(l); - if (RelationGetRelid(rInfo->ri_RelationDesc) == relid) + if (RelationGetRelid(rInfo->ri_RelationDesc) == relid && + rInfo->ri_RootResultRelInfo == rootRelInfo) return rInfo; } /* Nope, so we need a new one */ diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index dc541d61adf..7f9e0ebb82d 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -3406,3 +3406,54 @@ SET client_min_messages TO warning; DROP SCHEMA fkpart12 CASCADE; RESET client_min_messages; RESET search_path; +-- Exercise the column mapping code with foreign keys. In this test we'll +-- create a partitioned table which has a partition with a dropped column and +-- check to ensure that an UPDATE cascades the changes correctly to the +-- partitioned table. +CREATE SCHEMA fkpart13; +SET search_path TO fkpart13; +CREATE TABLE fkpart13_t1 (a int PRIMARY KEY); +CREATE TABLE fkpart13_t2 ( + part_id int PRIMARY KEY, + column_to_drop int, + FOREIGN KEY (part_id) REFERENCES fkpart13_t1 ON UPDATE CASCADE ON DELETE CASCADE +) PARTITION BY LIST (part_id); +CREATE TABLE fkpart13_t2_p1 PARTITION OF fkpart13_t2 FOR VALUES IN (1); +-- drop the column +ALTER TABLE fkpart13_t2 DROP COLUMN column_to_drop; +-- create a new partition without the dropped column +CREATE TABLE fkpart13_t2_p2 PARTITION OF fkpart13_t2 FOR VALUES IN (2); +CREATE TABLE fkpart13_t3 ( + a int NOT NULL, + FOREIGN KEY (a) + REFERENCES fkpart13_t2 + ON UPDATE CASCADE ON DELETE CASCADE +); +INSERT INTO fkpart13_t1 (a) VALUES (1); +INSERT INTO fkpart13_t2 (part_id) VALUES (1); +INSERT INTO fkpart13_t3 (a) VALUES (1); +-- Test a cascading update works correctly with with the dropped column +UPDATE fkpart13_t1 SET a = 2 WHERE a = 1; +SELECT tableoid::regclass,* FROM fkpart13_t2; + tableoid | part_id +----------------+--------- + fkpart13_t2_p2 | 2 +(1 row) + +SELECT tableoid::regclass,* FROM fkpart13_t3; + tableoid | a +-------------+--- + fkpart13_t3 | 2 +(1 row) + +-- Exercise code in ExecGetTriggerResultRel() as there's been previous issues +-- with ResultRelInfos being returned with the incorrect ri_RootResultRelInfo +WITH cte AS ( + UPDATE fkpart13_t2_p1 SET part_id = part_id +) UPDATE fkpart13_t1 SET a = 2 WHERE a = 1; +DROP SCHEMA fkpart13 CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table fkpart13_t1 +drop cascades to table fkpart13_t2 +drop cascades to table fkpart13_t3 +RESET search_path; diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 39174ad1eb9..4a6172b8e56 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -2386,3 +2386,51 @@ SET client_min_messages TO warning; DROP SCHEMA fkpart12 CASCADE; RESET client_min_messages; RESET search_path; + +-- Exercise the column mapping code with foreign keys. In this test we'll +-- create a partitioned table which has a partition with a dropped column and +-- check to ensure that an UPDATE cascades the changes correctly to the +-- partitioned table. +CREATE SCHEMA fkpart13; +SET search_path TO fkpart13; + +CREATE TABLE fkpart13_t1 (a int PRIMARY KEY); + +CREATE TABLE fkpart13_t2 ( + part_id int PRIMARY KEY, + column_to_drop int, + FOREIGN KEY (part_id) REFERENCES fkpart13_t1 ON UPDATE CASCADE ON DELETE CASCADE +) PARTITION BY LIST (part_id); + +CREATE TABLE fkpart13_t2_p1 PARTITION OF fkpart13_t2 FOR VALUES IN (1); + +-- drop the column +ALTER TABLE fkpart13_t2 DROP COLUMN column_to_drop; + +-- create a new partition without the dropped column +CREATE TABLE fkpart13_t2_p2 PARTITION OF fkpart13_t2 FOR VALUES IN (2); + +CREATE TABLE fkpart13_t3 ( + a int NOT NULL, + FOREIGN KEY (a) + REFERENCES fkpart13_t2 + ON UPDATE CASCADE ON DELETE CASCADE +); + +INSERT INTO fkpart13_t1 (a) VALUES (1); +INSERT INTO fkpart13_t2 (part_id) VALUES (1); +INSERT INTO fkpart13_t3 (a) VALUES (1); + +-- Test a cascading update works correctly with with the dropped column +UPDATE fkpart13_t1 SET a = 2 WHERE a = 1; +SELECT tableoid::regclass,* FROM fkpart13_t2; +SELECT tableoid::regclass,* FROM fkpart13_t3; + +-- Exercise code in ExecGetTriggerResultRel() as there's been previous issues +-- with ResultRelInfos being returned with the incorrect ri_RootResultRelInfo +WITH cte AS ( + UPDATE fkpart13_t2_p1 SET part_id = part_id +) UPDATE fkpart13_t1 SET a = 2 WHERE a = 1; + +DROP SCHEMA fkpart13 CASCADE; +RESET search_path; |
