From 84af557f2f6e5c181fec9efec3949f7876ea34ab Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Tue, 31 Mar 2026 20:00:45 +0900 Subject: [PATCH v1] 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 allocated in TopMemoryContext was not freed when the entry was invalidated in InvalidateConstraintCacheCallBack(), leaking memory each time the constraint cache entry was recycled. Fix by freeing fpmeta at invalidation time. Noticed locally when testing with CLOBBER_CACHE_ALWAYS. --- src/backend/utils/adt/ri_triggers.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index ffaa0e749cb..8673a4300a7 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -2486,6 +2486,11 @@ InvalidateConstraintCacheCallBack(Datum arg, SysCacheIdentifier cacheid, riinfo->rootHashValue == hashvalue) { riinfo->valid = false; + 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 +2719,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 +2743,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