]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix two issues in fast-path FK check introduced by commit 2da86c1ef9
authorAmit Langote <amitlan@postgresql.org>
Wed, 1 Apr 2026 08:29:33 +0000 (17:29 +0900)
committerAmit Langote <amitlan@postgresql.org>
Wed, 1 Apr 2026 08:30:33 +0000 (17:30 +0900)
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 moving the fpmeta initialization to after
ri_CheckPermissions(), reloading riinfo first to ensure it is
valid, then calling ri_ExtractValues() and build_index_scankeys()
immediately after 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 and NULLing fpmeta at invalidation time.

Noticed locally when testing with CLOBBER_CACHE_ALWAYS.

Discussion: https://postgr.es/m/CA+HiwqGBU__7-VZZhQWQ3EQuwLYNPd9==ngnzduhGWKHMj9mvw@mail.gmail.com

src/backend/utils/adt/ri_triggers.c

index 84d7bd68a72e9ff89e8518f210452e6bd3f6c752..52bb2f2fee9cd82586adc6c9bdc11e996f8b9662 100644 (file)
@@ -2488,6 +2488,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);
                }
@@ -2722,11 +2727,6 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
                                                           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 |
@@ -2734,6 +2734,14 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
                                                   SECURITY_NOFORCE_RLS);
        ri_CheckPermissions(pk_rel);
 
+       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);
        found = ri_FastPathProbeOne(pk_rel, idx_rel, scandesc, slot,