v2-0002-Cache-fast-path-metadata-for-foreign-key-checks.patch
application/octet-stream
Filename: v2-0002-Cache-fast-path-metadata-for-foreign-key-checks.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0002
Subject: Cache fast-path metadata for foreign key checks
| File | + | − |
|---|---|---|
| src/backend/utils/adt/ri_triggers.c | 65 | 25 |
From 020729139c824d65a008c6644431be8e8efd7800 Mon Sep 17 00:00:00 2001
From: Junwang Zhao <zhjwpku@gmail.com>
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