3030#include "funcapi.h"
3131#include "utils/funccache.h"
3232#include "utils/hsearch.h"
33+ #include "utils/inval.h"
3334#include "utils/syscache.h"
3435
3536
3637/*
3738 * Hash table for cached functions
3839 */
3940static HTAB * cfunc_hashtable = NULL ;
41+ static HTAB * cfunc_key_hashtable = NULL ;
42+
43+ static bool funccache_inval_registed = false;
4044
4145typedef struct CachedFunctionHashEntry
4246{
4347 CachedFunctionHashKey key ; /* hash key, must be first */
4448 CachedFunction * function ; /* points to data of language-specific size */
4549} CachedFunctionHashEntry ;
4650
51+ typedef struct CachedFunctionKeyEntry
52+ {
53+ uint32 keyhash ;
54+ CachedFunctionHashKey * key ;
55+ } CachedFunctionKeyEntry ;
56+
4757#define FUNCS_PER_USER 128 /* initial table size */
4858
4959static uint32 cfunc_hash (const void * key , Size keysize );
5060static int cfunc_match (const void * key1 , const void * key2 , Size keysize );
51-
61+ static uint32 cfuncoid_hash (Oid funcoid );
62+ static void InvalidateFuncPlanCacheCallback (Datum arg , int cacheid , uint32 hashvalue );
5263
5364/*
5465 * Initialize the hash table on first use.
@@ -67,10 +78,27 @@ cfunc_hashtable_init(void)
6778 ctl .entrysize = sizeof (CachedFunctionHashEntry );
6879 ctl .hash = cfunc_hash ;
6980 ctl .match = cfunc_match ;
81+ ctl .hcxt = CacheMemoryContext ;
7082 cfunc_hashtable = hash_create ("Cached function hash" ,
7183 FUNCS_PER_USER ,
7284 & ctl ,
73- HASH_ELEM | HASH_FUNCTION | HASH_COMPARE );
85+ HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT );
86+ }
87+
88+ static void
89+ cfunc_key_hashtable_init (void )
90+ {
91+ HASHCTL ctl2 ;
92+
93+ Assert (cfunc_key_hashtable == NULL );
94+
95+ ctl2 .keysize = sizeof (uint32 );
96+ ctl2 .entrysize = sizeof (CachedFunctionKeyEntry );
97+ ctl2 .hcxt = CacheMemoryContext ;
98+ cfunc_key_hashtable = hash_create ("Cached function hash" ,
99+ FUNCS_PER_USER ,
100+ & ctl2 ,
101+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
74102}
75103
76104/*
@@ -168,7 +196,9 @@ cfunc_hashtable_insert(CachedFunction *function,
168196 CachedFunctionHashKey * func_key )
169197{
170198 CachedFunctionHashEntry * hentry ;
199+ CachedFunctionKeyEntry * kentry ;
171200 bool found ;
201+ uint32 keyhash ;
172202
173203 if (cfunc_hashtable == NULL )
174204 cfunc_hashtable_init ();
@@ -198,6 +228,16 @@ cfunc_hashtable_insert(CachedFunction *function,
198228
199229 /* Set back-link from function to hashtable key */
200230 function -> fn_hashkey = & hentry -> key ;
231+
232+ /* Insert function key entry */
233+ if (cfunc_key_hashtable == NULL )
234+ cfunc_key_hashtable_init ();
235+
236+ keyhash = cfuncoid_hash (function -> fn_hashkey -> funcOid );
237+ kentry = (CachedFunctionKeyEntry * ) hash_search (cfunc_key_hashtable ,
238+ & keyhash ,
239+ HASH_ENTER , NULL );
240+ kentry -> key = function -> fn_hashkey ;
201241}
202242
203243/*
@@ -208,11 +248,16 @@ cfunc_hashtable_delete(CachedFunction *function)
208248{
209249 CachedFunctionHashEntry * hentry ;
210250 TupleDesc tupdesc ;
251+ uint32 keyhash ;
211252
212253 /* do nothing if not in table */
213254 if (function -> fn_hashkey == NULL )
214255 return ;
215256
257+ /* Release key hash table entry */
258+ keyhash = cfuncoid_hash (function -> fn_hashkey -> funcOid );
259+ hash_search (cfunc_key_hashtable , & keyhash , HASH_REMOVE , NULL );
260+
216261 /*
217262 * We need to free the callResultType if present, which is slightly tricky
218263 * because it has to be valid during the hashtable search. Fortunately,
@@ -332,6 +377,18 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
332377 }
333378}
334379
380+ static uint32
381+ cfuncoid_hash (Oid funcoid )
382+ {
383+ uint32 hashValue = 0 ;
384+ uint32 oneHash ;
385+
386+ oneHash = murmurhash32 ((uint32 ) funcoid );
387+ hashValue ^= oneHash ;
388+
389+ return hashValue ;
390+ }
391+
335392/*
336393 * This is the same as the standard resolve_polymorphic_argtypes() function,
337394 * except that:
@@ -493,6 +550,16 @@ cached_function_compile(FunctionCallInfo fcinfo,
493550 bool hashkey_valid = false;
494551 bool new_function = false;
495552
553+ /*
554+ * Register catalog invalidate callback
555+ */
556+ if (!funccache_inval_registed )
557+ {
558+ funccache_inval_registed = true;
559+
560+ CacheRegisterSyscacheCallback (PROCOID , InvalidateFuncPlanCacheCallback , (Datum ) 0 );
561+ }
562+
496563 /*
497564 * Lookup the pg_proc tuple by Oid; we'll need it in any case
498565 */
@@ -632,3 +699,48 @@ cached_function_compile(FunctionCallInfo fcinfo,
632699 */
633700 return function ;
634701}
702+
703+
704+ /*
705+ * Try to delete and release the plan cache from the hash table.
706+ */
707+ static void
708+ InvalidateFuncPlanCacheCallback (Datum arg , int cacheid , uint32 hashvalue )
709+ {
710+ Assert (cacheid == PROCOID );
711+
712+ if (cfunc_hashtable == NULL || cfunc_key_hashtable == NULL )
713+ return ;
714+
715+ if (hashvalue == 0 )
716+ {
717+ HASH_SEQ_STATUS scan ;
718+ CachedFunctionHashEntry * hentry ;
719+
720+ hash_seq_init (& scan , cfunc_hashtable );
721+ while ((hentry = (CachedFunctionHashEntry * ) hash_seq_search (& scan )))
722+ {
723+ if (hentry -> function == NULL )
724+ continue ;
725+
726+ /* ... and free */
727+ delete_function (hentry -> function );
728+ }
729+ }
730+ else
731+ {
732+ CachedFunctionKeyEntry * kentry ;
733+ CachedFunction * function ;
734+ bool found ;
735+
736+ kentry = (CachedFunctionKeyEntry * ) hash_search (cfunc_key_hashtable ,
737+ & hashvalue , HASH_FIND , & found );
738+ if (found )
739+ {
740+ function = cfunc_hashtable_lookup (kentry -> key );
741+
742+ if (function )
743+ delete_function (function );
744+ }
745+ }
746+ }
0 commit comments