002_cleanup_spl_funccache_v2.patch
application/octet-stream
Filename: 002_cleanup_spl_funccache_v2.patch
Type: application/octet-stream
Part: 0
diff --git a/src/backend/utils/cache/funccache.c b/src/backend/utils/cache/funccache.c
index afc048a051e..94f7c083df6 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 @@ recheck:
*/
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 d8ce39dba3c..2498a988e12 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -4593,6 +4593,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'