From d4d26dd30bd4ee51599b6dbdf347a5da5c9e4c91 Mon Sep 17 00:00:00 2001 From: Commitfest Bot Date: Tue, 4 Nov 2025 10:07:20 +0000 Subject: [PATCH] [PATCH]: ./002_cleanup_spl_funccache_v2.patch --- src/backend/utils/cache/funccache.c | 116 +++++++++++++++++++++++++- src/test/regress/expected/plpgsql.out | 4 + 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/cache/funccache.c b/src/backend/utils/cache/funccache.c index afc048a051ea..94f7c083df6f 100644 --- a/src/backend/utils/cache/funccache.c +++ b/src/backend/utils/cache/funccache.c @@ -30,6 +30,7 @@ #include "funcapi.h" #include "utils/funccache.h" #include "utils/hsearch.h" +#include "utils/inval.h" #include "utils/syscache.h" @@ -37,6 +38,9 @@ * Hash table for cached functions */ static HTAB *cfunc_hashtable = NULL; +static HTAB *cfunc_key_hashtable = NULL; + +static bool funccache_inval_registed = false; typedef struct CachedFunctionHashEntry { @@ -44,11 +48,18 @@ typedef struct CachedFunctionHashEntry CachedFunction *function; /* points to data of language-specific size */ } CachedFunctionHashEntry; +typedef struct CachedFunctionKeyEntry +{ + uint32 keyhash; + CachedFunctionHashKey *key; +} CachedFunctionKeyEntry; + #define FUNCS_PER_USER 128 /* initial table size */ static uint32 cfunc_hash(const void *key, Size keysize); static int cfunc_match(const void *key1, const void *key2, Size keysize); - +static uint32 cfuncoid_hash(Oid funcoid); +static void InvalidateFuncPlanCacheCallback(Datum arg, int cacheid, uint32 hashvalue); /* * Initialize the hash table on first use. @@ -67,10 +78,27 @@ cfunc_hashtable_init(void) ctl.entrysize = sizeof(CachedFunctionHashEntry); ctl.hash = cfunc_hash; ctl.match = cfunc_match; + ctl.hcxt = CacheMemoryContext; cfunc_hashtable = hash_create("Cached function hash", FUNCS_PER_USER, &ctl, - HASH_ELEM | HASH_FUNCTION | HASH_COMPARE); + HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT); +} + +static void +cfunc_key_hashtable_init(void) +{ + HASHCTL ctl2; + + Assert(cfunc_key_hashtable == NULL); + + ctl2.keysize = sizeof(uint32); + ctl2.entrysize = sizeof(CachedFunctionKeyEntry); + ctl2.hcxt = CacheMemoryContext; + cfunc_key_hashtable = hash_create("Cached function hash", + FUNCS_PER_USER, + &ctl2, + HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); } /* @@ -168,7 +196,9 @@ cfunc_hashtable_insert(CachedFunction *function, CachedFunctionHashKey *func_key) { CachedFunctionHashEntry *hentry; + CachedFunctionKeyEntry *kentry; bool found; + uint32 keyhash; if (cfunc_hashtable == NULL) cfunc_hashtable_init(); @@ -198,6 +228,16 @@ cfunc_hashtable_insert(CachedFunction *function, /* Set back-link from function to hashtable key */ function->fn_hashkey = &hentry->key; + + /* Insert function key entry */ + if (cfunc_key_hashtable == NULL) + cfunc_key_hashtable_init(); + + keyhash = cfuncoid_hash(function->fn_hashkey->funcOid); + kentry = (CachedFunctionKeyEntry *) hash_search(cfunc_key_hashtable, + &keyhash, + HASH_ENTER, NULL); + kentry->key = function->fn_hashkey; } /* @@ -208,11 +248,16 @@ cfunc_hashtable_delete(CachedFunction *function) { CachedFunctionHashEntry *hentry; TupleDesc tupdesc; + uint32 keyhash; /* do nothing if not in table */ if (function->fn_hashkey == NULL) return; + /* Release key hash table entry */ + keyhash = cfuncoid_hash(function->fn_hashkey->funcOid); + hash_search(cfunc_key_hashtable, &keyhash, HASH_REMOVE, NULL); + /* * We need to free the callResultType if present, which is slightly tricky * because it has to be valid during the hashtable search. Fortunately, @@ -332,6 +377,18 @@ compute_function_hashkey(FunctionCallInfo fcinfo, } } +static uint32 +cfuncoid_hash(Oid funcoid) +{ + uint32 hashValue = 0; + uint32 oneHash; + + oneHash = murmurhash32((uint32) funcoid); + hashValue ^= oneHash; + + return hashValue; +} + /* * This is the same as the standard resolve_polymorphic_argtypes() function, * except that: @@ -493,6 +550,16 @@ cached_function_compile(FunctionCallInfo fcinfo, bool hashkey_valid = false; bool new_function = false; + /* + * Register catalog invalidate callback + */ + if (!funccache_inval_registed) + { + funccache_inval_registed = true; + + CacheRegisterSyscacheCallback(PROCOID, InvalidateFuncPlanCacheCallback, (Datum) 0); + } + /* * Lookup the pg_proc tuple by Oid; we'll need it in any case */ @@ -632,3 +699,48 @@ cached_function_compile(FunctionCallInfo fcinfo, */ return function; } + + +/* + * Try to delete and release the plan cache from the hash table. + */ +static void +InvalidateFuncPlanCacheCallback(Datum arg, int cacheid, uint32 hashvalue) +{ + Assert(cacheid == PROCOID); + + if (cfunc_hashtable == NULL || cfunc_key_hashtable == NULL) + return; + + if (hashvalue == 0) + { + HASH_SEQ_STATUS scan; + CachedFunctionHashEntry *hentry; + + hash_seq_init(&scan, cfunc_hashtable); + while ((hentry = (CachedFunctionHashEntry *) hash_seq_search(&scan))) + { + if (hentry->function == NULL) + continue; + + /* ... and free */ + delete_function(hentry->function); + } + } + else + { + CachedFunctionKeyEntry *kentry; + CachedFunction *function; + bool found; + + kentry = (CachedFunctionKeyEntry *) hash_search(cfunc_key_hashtable, + &hashvalue, HASH_FIND, &found); + if (found) + { + function = cfunc_hashtable_lookup(kentry->key); + + if (function) + delete_function(function); + } + } +} \ No newline at end of file diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 474be478ce8e..deda2dbb81f5 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -4596,6 +4596,10 @@ LINE 4: return 'foo\\bar\041baz'; ^ HINT: Use the escape string syntax for backslashes, e.g., E'\\'. select strtest(); +WARNING: nonstandard use of \\ in a string literal +HINT: Use the escape string syntax for backslashes, e.g., E'\\'. +WARNING: nonstandard use of \\ in a string literal +HINT: Use the escape string syntax for backslashes, e.g., E'\\'. NOTICE: foo\bar!baz WARNING: nonstandard use of \\ in a string literal LINE 1: 'foo\\bar\041baz'