From 35b5a8339f88aa4dcd857af7555e514ab5b8438b Mon Sep 17 00:00:00 2001 From: "Paul A. Jungwirth" Date: Fri, 13 Jun 2025 16:11:47 -0700 Subject: [PATCH v62 5/8] Look up additional temporal foreign key helper proc To implement CASCADE/SET NULL/SET DEFAULT on temporal foreign keys, we need an intersect function. We can look them it when we look up the operators already needed for temporal foreign keys (including NO ACTION constraints). Author: Paul A. Jungwirth --- src/backend/catalog/pg_constraint.c | 32 ++++++++++++++++++++++++----- src/backend/commands/tablecmds.c | 5 +++-- src/backend/parser/analyze.c | 2 +- src/backend/utils/adt/ri_triggers.c | 10 +++++---- src/include/catalog/pg_constraint.h | 9 ++++---- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 9944e4bd2d1..565c9084994 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -1635,7 +1635,7 @@ DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, } /* - * FindFKPeriodOpers - + * FindFKPeriodOpersAndProcs - * * Looks up the operator oids used for the PERIOD part of a temporal foreign key. * The opclass should be the opclass of that PERIOD element. @@ -1646,12 +1646,15 @@ DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, * That way foreign keys can compare fkattr <@ range_agg(pkattr). * intersectoperoid is used by NO ACTION constraints to trim the range being considered * to just what was updated/deleted. + * intersectprocoid is used to limit the effect of CASCADE/SET NULL/SET DEFAULT + * when the PK record is changed with FOR PORTION OF. */ void -FindFKPeriodOpers(Oid opclass, - Oid *containedbyoperoid, - Oid *aggedcontainedbyoperoid, - Oid *intersectoperoid) +FindFKPeriodOpersAndProcs(Oid opclass, + Oid *containedbyoperoid, + Oid *aggedcontainedbyoperoid, + Oid *intersectoperoid, + Oid *intersectprocoid) { Oid opfamily = InvalidOid; Oid opcintype = InvalidOid; @@ -1693,6 +1696,17 @@ FindFKPeriodOpers(Oid opclass, aggedcontainedbyoperoid, &strat); + /* + * Hardcode intersect operators for ranges and multiranges, because we + * don't have a better way to look up operators that aren't used in + * indexes. + * + * If you change this code, you must change the code in + * transformForPortionOfClause. + * + * XXX: Find a more extensible way to look up the operator, permitting + * user-defined types. + */ switch (opcintype) { case ANYRANGEOID: @@ -1704,6 +1718,14 @@ FindFKPeriodOpers(Oid opclass, default: elog(ERROR, "unexpected opcintype: %u", opcintype); } + + /* + * Look up the intersect proc. We use this in temporal foreign keys with + * CASCADE/SET NULL/SET DEFAULT to build the FOR PORTION OF bounds. If this + * is missing we don't need to complain here, because FOR PORTION OF will + * not be allowed. + */ + *intersectprocoid = get_opcode(*intersectoperoid); } /* diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 23ebaa3f230..61e63bd9926 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -10564,9 +10564,10 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid periodoperoid; Oid aggedperiodoperoid; Oid intersectoperoid; + Oid intersectprocoid; - FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid, - &intersectoperoid); + FindFKPeriodOpersAndProcs(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid, + &intersectoperoid, &intersectprocoid); } /* First, create the constraint catalog entry itself. */ diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 3efedd678c0..33ec0aba28d 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1461,7 +1461,7 @@ transformForPortionOfClause(ParseState *pstate, /* * Whatever operator is used for intersect by temporal foreign keys, * we can use its backing procedure for intersects in FOR PORTION OF. - * XXX: Share code with FindFKPeriodOpers? + * XXX: Share code with FindFKPeriodOpersAndProcs? */ switch (opcintype) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index d6b1eb57a48..69f7de25e23 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -129,6 +129,7 @@ typedef struct RI_ConstraintInfo Oid period_contained_by_oper; /* anyrange <@ anyrange */ Oid agged_period_contained_by_oper; /* fkattr <@ range_agg(pkattr) */ Oid period_intersect_oper; /* anyrange * anyrange (or multirange) */ + Oid period_intersect_proc; /* anyrange * anyrange (or multirange) */ dlist_node valid_link; /* Link in list of valid entries */ } RI_ConstraintInfo; @@ -2337,10 +2338,11 @@ ri_LoadConstraintInfo(Oid constraintOid) { Oid opclass = get_index_column_opclass(conForm->conindid, riinfo->nkeys); - FindFKPeriodOpers(opclass, - &riinfo->period_contained_by_oper, - &riinfo->agged_period_contained_by_oper, - &riinfo->period_intersect_oper); + FindFKPeriodOpersAndProcs(opclass, + &riinfo->period_contained_by_oper, + &riinfo->agged_period_contained_by_oper, + &riinfo->period_intersect_oper, + &riinfo->period_intersect_proc); } ReleaseSysCache(tup); diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 4afceb5c692..9b58f618f9e 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -288,10 +288,11 @@ extern void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols); -extern void FindFKPeriodOpers(Oid opclass, - Oid *containedbyoperoid, - Oid *aggedcontainedbyoperoid, - Oid *intersectoperoid); +extern void FindFKPeriodOpersAndProcs(Oid opclass, + Oid *containedbyoperoid, + Oid *aggedcontainedbyoperoid, + Oid *intersectoperoid, + Oid *intersectprocoid); extern bool check_functional_grouping(Oid relid, Index varno, Index varlevelsup, -- 2.47.3