From 020729139c824d65a008c6644431be8e8efd7800 Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Mon, 1 Dec 2025 12:58:59 +0800 Subject: [PATCH v2 2/2] Cache fast-path metadata for foreign key checks The metadata is populated lazily on first use via ri_populate_fastpath_metadata() and reused in subsequent checks via build_scankeys_from_cache(). This eliminates repeated calls to ri_HashCompareOp() and get_op_opfamily_properties() during FK checks. --- src/backend/utils/adt/ri_triggers.c | 90 +++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index cfb85b9d753..f2e7e4f4ae9 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -94,6 +94,7 @@ #define RI_TRIGTYPE_UPDATE 2 #define RI_TRIGTYPE_DELETE 3 +struct RI_CompareHashEntry; /* * RI_ConstraintInfo @@ -133,6 +134,16 @@ typedef struct RI_ConstraintInfo Oid agged_period_contained_by_oper; /* fkattr <@ range_agg(pkattr) */ Oid period_intersect_oper; /* anyrange * anyrange */ dlist_node valid_link; /* Link in list of valid entries */ + + /* Fast-path metadata for RI checks on foreign tables */ + bool fpmeta_valid; /* is fast-path metadata valid? */ + // Relation idxrel; + // IndexScanDesc idxscan; + // TupleTableSlot *outslot; + struct RI_CompareHashEntry *compare_entries[RI_MAX_NUMKEYS]; + RegProcedure regops[RI_MAX_NUMKEYS]; + Oid subtypes[RI_MAX_NUMKEYS]; + int strats[RI_MAX_NUMKEYS]; } RI_ConstraintInfo; /* @@ -295,6 +306,42 @@ get_fkey_unique_index(Oid conoid) return result; } +static void +ri_populate_fastpath_metadata(Oid constraintOid, + Relation pk_rel, Relation fk_rel, Relation idx_rel) +{ + RI_ConstraintInfo *riinfo; + + /* Find the constraint info */ + riinfo = (RI_ConstraintInfo *) + hash_search(ri_constraint_cache, + &constraintOid, + HASH_FIND, + NULL); + Assert(riinfo != NULL && riinfo->valid); + + for (int i = 0; i < riinfo->nkeys; i++) + { + /* Use PK = FK equality operator. */ + Oid eq_opr = riinfo->pf_eq_oprs[i]; + Oid typeid = RIAttType(fk_rel, riinfo->fk_attnums[i]); + Oid lefttype; + RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid); + + riinfo->compare_entries[i] = entry; + riinfo->regops[i] = get_opcode(eq_opr); + + get_op_opfamily_properties(eq_opr, + idx_rel->rd_opfamily[i], + false, + &riinfo->strats[i], + &lefttype, + &riinfo->subtypes[i]); + } + + riinfo->fpmeta_valid = true; +} + /* * ri_CheckPermissions * Check that the new user has permissions to look into the schema of @@ -365,20 +412,14 @@ recheck_matched_pk_tuple(Relation idxrel, ScanKeyData *skeys, } /* - * Doesn't include any cache for now. + * Build ScanKeys from cached metadata for fast-path foreign key checks */ static void build_scankeys_from_cache(const RI_ConstraintInfo *riinfo, Relation pk_rel, Relation fk_rel, - Relation idx_rel, int num_pk, - Datum *pk_vals, char *pk_nulls, - ScanKey skeys) + Relation idx_rel, Datum *pk_vals, + char *pk_nulls, ScanKey skeys) { - /* Use PK = FK equality operator. */ - const Oid *eq_oprs = riinfo->pf_eq_oprs; - - Assert(num_pk == riinfo->nkeys); - /* * May need to cast each of the individual values of the foreign key * to the corresponding PK column's type if the equality operator @@ -388,9 +429,7 @@ build_scankeys_from_cache(const RI_ConstraintInfo *riinfo, { if (pk_nulls[i] != 'n') { - Oid eq_opr = eq_oprs[i]; - Oid typeid = RIAttType(fk_rel, riinfo->fk_attnums[i]); - RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid); + RI_CompareHashEntry *entry = riinfo->compare_entries[i]; if (OidIsValid(entry->cast_func_finfo.fn_oid)) pk_vals[i] = FunctionCall3(&entry->cast_func_finfo, @@ -406,20 +445,12 @@ build_scankeys_from_cache(const RI_ConstraintInfo *riinfo, * Set up ScanKeys for the index scan. This is essentially how * ExecIndexBuildScanKeys() sets them up. */ - for (int i = 0; i < num_pk; i++) + for (int i = 0; i < riinfo->nkeys; i++) { int pkattrno = i + 1; - Oid lefttype, - righttype; - Oid operator = eq_oprs[i]; - Oid opfamily = idx_rel->rd_opfamily[i]; - int strat; - RegProcedure regop = get_opcode(operator); - - get_op_opfamily_properties(operator, opfamily, false, &strat, - &lefttype, &righttype); - ScanKeyEntryInitialize(&skeys[i], 0, pkattrno, strat, righttype, - idx_rel->rd_indcollation[i], regop, + + ScanKeyEntryInitialize(&skeys[i], 0, pkattrno, riinfo->strats[i], riinfo->subtypes[i], + idx_rel->rd_indcollation[i], riinfo->regops[i], pk_vals[i]); } } @@ -583,7 +614,15 @@ RI_FKey_check(TriggerData *trigdata) idxrel = index_open(idxoid, RowShareLock); num_pk = IndexRelationGetNumberOfKeyAttributes(idxrel); - build_scankeys_from_cache(riinfo, pk_rel, fk_rel, idxrel, num_pk, + Assert(num_pk == riinfo->nkeys); + + /* If Fast-path metadata hasn't been populated, do it now */ + if (!riinfo->fpmeta_valid) + ri_populate_fastpath_metadata(riinfo->constraint_id, + pk_rel, fk_rel, idxrel); + Assert(riinfo->fpmeta_valid); + + build_scankeys_from_cache(riinfo, pk_rel, fk_rel, idxrel, pk_vals, pk_nulls, skey); scan = index_beginscan(pk_rel, idxrel, GetActiveSnapshot(), NULL, riinfo->nkeys, 0); @@ -2663,6 +2702,7 @@ ri_LoadConstraintInfo(Oid constraintOid) dclist_push_tail(&ri_constraint_cache_valid_list, &riinfo->valid_link); riinfo->valid = true; + riinfo->fpmeta_valid = false; return riinfo; } -- 2.41.0