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
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