From 0a4187aa72bae647f621ea2462469ff409c00757 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Wed, 1 Apr 2026 15:53:13 +0900
Subject: [PATCH v14 1/2] Make FastPathMeta self-contained by copying FmgrInfo
 structs

FastPathMeta stored pointers into ri_compare_cache entries via
compare_entries[], creating a dependency on that cache remaining
stable.  If ri_compare_cache entries were invalidated after fpmeta
was populated, the pointers would dangle.

Replace compare_entries[] with inline copies of the two FmgrInfo
fields actually needed (cast_func_finfo and eq_opr_finfo), copied
at populate time via fmgr_info_copy().  fpmeta now depends only on
riinfo remaining valid, which is already handled by the invalidation
callback.

This issue was introduced by commit 2da86c1ef9 ("Add fast path for
foreign key constraint checks").

Discussion: https://postgr.es/m/CA+HiwqF4C0ws3cO+z5cLkPuvwnAwkSp7sfvgGj3yQ=Li6KNMqA@mail.gmail.com
---
 src/backend/utils/adt/ri_triggers.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 52bb2f2fee9..2de08da6539 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -150,7 +150,8 @@ typedef struct RI_CompareHashEntry RI_CompareHashEntry;
 /* Fast-path metadata for RI checks on foreign key referencing tables */
 typedef struct FastPathMeta
 {
-	RI_CompareHashEntry *compare_entries[RI_MAX_NUMKEYS];
+	FmgrInfo	eq_opr_finfo[RI_MAX_NUMKEYS];
+	FmgrInfo	cast_func_finfo[RI_MAX_NUMKEYS];
 	RegProcedure regops[RI_MAX_NUMKEYS];
 	Oid			subtypes[RI_MAX_NUMKEYS];
 	int			strats[RI_MAX_NUMKEYS];
@@ -2996,16 +2997,12 @@ build_index_scankeys(const RI_ConstraintInfo *riinfo,
 	 */
 	for (int i = 0; i < riinfo->nkeys; i++)
 	{
-		if (pk_nulls[i] != 'n')
-		{
-			RI_CompareHashEntry *entry = fpmeta->compare_entries[i];
-
-			if (OidIsValid(entry->cast_func_finfo.fn_oid))
-				pk_vals[i] = FunctionCall3(&entry->cast_func_finfo,
-										   pk_vals[i],
-										   Int32GetDatum(-1),	/* typmod */
-										   BoolGetDatum(false));	/* implicit coercion */
-		}
+		if (pk_nulls[i] != 'n' &&
+			OidIsValid(fpmeta->cast_func_finfo[i].fn_oid))
+			pk_vals[i] = FunctionCall3(&fpmeta->cast_func_finfo[i],
+									   pk_vals[i],
+									   Int32GetDatum(-1),	/* typmod */
+									   BoolGetDatum(false));	/* implicit coercion */
 	}
 
 	/*
@@ -3048,7 +3045,10 @@ ri_populate_fastpath_metadata(RI_ConstraintInfo *riinfo,
 		Oid			lefttype;
 		RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
 
-		fpmeta->compare_entries[i] = entry;
+		fmgr_info_copy(&fpmeta->cast_func_finfo[i], &entry->cast_func_finfo,
+					   CurrentMemoryContext);
+		fmgr_info_copy(&fpmeta->eq_opr_finfo[i], &entry->eq_opr_finfo,
+					   CurrentMemoryContext);
 		fpmeta->regops[i] = get_opcode(eq_opr);
 
 		get_op_opfamily_properties(eq_opr,
-- 
2.47.3

