v13-0001-Fix-two-issues-in-fast-path-FK-check-introduced-.patch

application/octet-stream

Filename: v13-0001-Fix-two-issues-in-fast-path-FK-check-introduced-.patch
Type: application/octet-stream
Part: 0
Message: Re: Eliminating SPI / SQL from some RI triggers - take 3

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 v13-0001
Subject: Fix two issues in fast-path FK check introduced by commit 2da86c1ef9
File+
src/backend/utils/adt/ri_triggers.c 21 7
From 5ea789323d2005b6df8e0adc88151823e1fbed28 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Tue, 31 Mar 2026 20:53:43 +0900
Subject: [PATCH v13 1/2] Fix two issues in fast-path FK check introduced by
 commit 2da86c1ef9

First, under CLOBBER_CACHE_ALWAYS, the RI_ConstraintInfo entry can
be invalidated by relcache callbacks triggered inside table_open()
or index_open(), leaving ri_FastPathCheck() calling
ri_populate_fastpath_metadata() with a stale entry whose valid flag
is false.  Fix by reloading riinfo after the relation opens and
populating fpmeta immediately, then calling ri_ExtractValues() and
build_index_scankeys() before any further operations that could
trigger invalidation.

Second, fpmeta was not freed or cleared when the entry was
invalidated in InvalidateConstraintCacheCallBack(), leaving a
dangling pointer that caused a crash on the next invocation when
the guard `if (riinfo->fpmeta == NULL)` incorrectly skipped
repopulation.  Fix by freeing and NULLing fpmeta at invalidation
time.

Noticed locally when testing with CLOBBER_CACHE_ALWAYS.
---
 src/backend/utils/adt/ri_triggers.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index ffaa0e749cb..d57343f85d8 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2486,6 +2486,16 @@ InvalidateConstraintCacheCallBack(Datum arg, SysCacheIdentifier cacheid,
 			riinfo->rootHashValue == hashvalue)
 		{
 			riinfo->valid = false;
+			/*
+			 * Free and clear any cached fast-path metadata so the next use
+			 * repopulates it from scratch rather than following a dangling
+			 * pointer.
+			 */
+			if (riinfo->fpmeta)
+			{
+				pfree(riinfo->fpmeta);
+				riinfo->fpmeta = NULL;
+			}
 			/* Remove invalidated entries from the list, too */
 			dclist_delete_from(&ri_constraint_cache_valid_list, iter.cur);
 		}
@@ -2714,17 +2724,23 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
 	pk_rel = table_open(riinfo->pk_relid, RowShareLock);
 	idx_rel = index_open(riinfo->conindid, AccessShareLock);
 
+	if (riinfo->fpmeta == NULL)
+	{
+		/* Reload to ensure it's valid. */
+		riinfo = ri_LoadConstraintInfo(riinfo->constraint_id);
+		ri_populate_fastpath_metadata((RI_ConstraintInfo *) riinfo,
+									  fk_rel, idx_rel);
+	}
+	Assert(riinfo->fpmeta);
+	ri_ExtractValues(fk_rel, newslot, riinfo, false, pk_vals, pk_nulls);
+	build_index_scankeys(riinfo, idx_rel, pk_vals, pk_nulls, skey);
+
 	slot = table_slot_create(pk_rel, NULL);
 	scandesc = index_beginscan(pk_rel, idx_rel,
 							   snapshot, NULL,
 							   riinfo->nkeys, 0,
 							   SO_NONE);
 
-	if (riinfo->fpmeta == NULL)
-		ri_populate_fastpath_metadata((RI_ConstraintInfo *) riinfo,
-									  fk_rel, idx_rel);
-	Assert(riinfo->fpmeta);
-
 	GetUserIdAndSecContext(&saved_userid, &saved_sec_context);
 	SetUserIdAndSecContext(RelationGetForm(pk_rel)->relowner,
 						   saved_sec_context |
@@ -2732,8 +2748,6 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
 						   SECURITY_NOFORCE_RLS);
 	ri_CheckPermissions(pk_rel);
 
-	ri_ExtractValues(fk_rel, newslot, riinfo, false, pk_vals, pk_nulls);
-	build_index_scankeys(riinfo, idx_rel, pk_vals, pk_nulls, skey);
 	found = ri_FastPathProbeOne(pk_rel, idx_rel, scandesc, slot,
 								snapshot, riinfo, skey, riinfo->nkeys);
 	SetUserIdAndSecContext(saved_userid, saved_sec_context);
-- 
2.47.3