From e45de721568879a9625fcd7b459bff94931b7586 Mon Sep 17 00:00:00 2001 From: "Paul A. Jungwirth" Date: Fri, 13 Jun 2025 16:11:47 -0700 Subject: [PATCH v1 3/7] Look up additional temporal foreign key helper procs To implement CASCADE/SET NULL/SET DEFAULT on temporal foreign keys, we need an intersect and without_portion function. We can look them up when we look up the operators already needed for NO ACTION temporal foreign keys. Author: Paul A. Jungwirth --- src/backend/catalog/pg_constraint.c | 37 +++++++++++++++++++++++++---- src/backend/commands/tablecmds.c | 7 ++++-- src/backend/parser/analyze.c | 2 +- src/backend/utils/adt/ri_triggers.c | 15 ++++++++---- src/include/catalog/pg_constraint.h | 10 ++++---- 5 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 48ede21007e..fd14df6cace 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -1655,7 +1655,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. @@ -1666,12 +1666,18 @@ 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 + * to the history the PK record kept. + * withoutportionprocoid is a set-returning function giving the history the PK + * record lost, so that CASCADE can delete it from the referencing rows. */ void -FindFKPeriodOpers(Oid opclass, - Oid *containedbyoperoid, - Oid *aggedcontainedbyoperoid, - Oid *intersectoperoid) +FindFKPeriodOpersAndProcs(Oid opclass, + Oid *containedbyoperoid, + Oid *aggedcontainedbyoperoid, + Oid *intersectoperoid, + Oid *intersectprocoid, + Oid *withoutportionprocoid) { Oid opfamily = InvalidOid; Oid opcintype = InvalidOid; @@ -1713,17 +1719,38 @@ FindFKPeriodOpers(Oid opclass, aggedcontainedbyoperoid, &strat); + /* + * Hardcode the intersect operator and the without_portion function for + * ranges and multiranges, because we don't have a better way to look up + * things 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 these up, permitting + * user-defined types. + */ switch (opcintype) { case ANYRANGEOID: *intersectoperoid = OID_RANGE_INTERSECT_RANGE_OP; + *withoutportionprocoid = F_RANGE_MINUS_MULTI; break; case ANYMULTIRANGEOID: *intersectoperoid = OID_MULTIRANGE_INTERSECT_MULTIRANGE_OP; + *withoutportionprocoid = F_MULTIRANGE_MINUS_MULTI; break; 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 fd144d783d9..1baa6c05718 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -10669,9 +10669,12 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid periodoperoid; Oid aggedperiodoperoid; Oid intersectoperoid; + Oid intersectprocoid; + Oid withoutportionprocoid; - FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid, - &intersectoperoid); + FindFKPeriodOpersAndProcs(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid, + &intersectoperoid, &intersectprocoid, + &withoutportionprocoid); } /* First, create the constraint catalog entry itself. */ diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 0be92fb7013..303a32d7141 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1554,7 +1554,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 d4545618634..00ff0f949b9 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -140,6 +140,11 @@ typedef struct RI_ConstraintInfo Oid agged_period_contained_by_oper; /* fkattr <@ range_agg(pkattr) */ Oid period_intersect_oper; /* anyrange * anyrange (or * multiranges) */ + Oid period_intersect_proc; /* anyrange * anyrange (or + * multiranges) */ + Oid period_without_portion_proc; /* anyrange minus anyrange, + * returning SETOF anyrange + * (or multiranges) */ dlist_node valid_link; /* Link in list of valid entries */ Oid conindid; @@ -2537,10 +2542,12 @@ 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, + &riinfo->period_without_portion_proc); } /* Metadata used by fast path. */ diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 1b7fedf1750..7d81b0257d6 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -292,10 +292,12 @@ 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, + Oid *withoutportionprocoid); extern bool check_functional_grouping(Oid relid, Index varno, Index varlevelsup, -- 2.45.0