diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index f63b5ef420b..7604f1a0064 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -1301,6 +1301,7 @@ RemoveFunctionById(Oid funcOid) Relation relation; HeapTuple tup; char prokind; + remove_function_plancache RemoveFunctionPlanCacheById; /* * Delete the pg_proc tuple. @@ -1311,6 +1312,11 @@ RemoveFunctionById(Oid funcOid) if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", funcOid); + /* Try to release plancache */ + RemoveFunctionPlanCacheById = (remove_function_plancache) *find_rendezvous_variable("RemoveFunctionPlanCacheById"); + if (RemoveFunctionPlanCacheById != NULL) + RemoveFunctionPlanCacheById(funcOid); + prokind = ((Form_pg_proc) GETSTRUCT(tup))->prokind; CatalogTupleDelete(relation, &tup->t_self); diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h index a443181d416..a8e6388a330 100644 --- a/src/include/utils/plancache.h +++ b/src/include/utils/plancache.h @@ -233,4 +233,5 @@ extern bool CachedPlanIsSimplyValid(CachedPlanSource *plansource, extern CachedExpression *GetCachedExpression(Node *expr); extern void FreeCachedExpression(CachedExpression *cexpr); +typedef void (*remove_function_plancache) (Oid funcoid); #endif /* PLANCACHE_H */ diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index a341cde2c14..12adcc37ef9 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -67,6 +67,23 @@ typedef struct plpgsql_hashent PLpgSQL_function *function; } plpgsql_HashEnt; +/* ---------- + * Hash table for recording funcOid and functions + * ---------- + */ +static HTAB *plpgsql_funcOid_HashTable = NULL; + +typedef struct PLpgSQL_funcOid_hashkey +{ + Oid funcOid; +} PLpgSQL_funcOid_hashkey; + +typedef struct plpgsql_function_hashent +{ + PLpgSQL_funcOid_hashkey key; + PLpgSQL_function *function; +} plpgsql_function_hashent; + #define FUNCS_PER_USER 128 /* initial table size */ /* ---------- @@ -121,6 +138,9 @@ static void plpgsql_HashTableInsert(PLpgSQL_function *function, PLpgSQL_func_hashkey *func_key); static void plpgsql_HashTableDelete(PLpgSQL_function *function); static void delete_function(PLpgSQL_function *func); +static PLpgSQL_function *plpgsql_funcOid_HashTableLookup(PLpgSQL_funcOid_hashkey *func_key); +static void plpgsql_funcOid_HashTableInsert(PLpgSQL_function *function); +static void plpgsql_funcOid_HashTableDelete(PLpgSQL_funcOid_hashkey *func_key); /* ---------- * plpgsql_compile Make an execution tree for a PL/pgSQL function. @@ -223,6 +243,9 @@ recheck: */ function = do_compile(fcinfo, procTup, function, &hashkey, forValidator); + + /* Record the relationship between funcOid and function */ + plpgsql_funcOid_HashTableInsert(function); } ReleaseSysCache(procTup); @@ -2670,3 +2693,87 @@ plpgsql_HashTableDelete(PLpgSQL_function *function) /* remove back link, which no longer points to allocated storage */ function->fn_hashkey = NULL; } + +/* Try to delete and release the plan cache from the hash table */ +void +RemoveFunctionPlanCacheById(Oid funcoid) +{ + PLpgSQL_funcOid_hashkey func_key; + PLpgSQL_function *function = NULL; + + func_key.funcOid = funcoid; + function = plpgsql_funcOid_HashTableLookup(&func_key); + if (function) + { + plpgsql_funcOid_HashTableDelete(&func_key); + delete_function(function); + } +} + +void +plpgsql_funcOid_HashTableInit(void) +{ + HASHCTL ctl; + + /* don't allow double-initialization */ + Assert(plpgsql_funcOid_HashTable == NULL); + + ctl.keysize = sizeof(PLpgSQL_funcOid_hashkey); + ctl.entrysize = sizeof(plpgsql_function_hashent); + plpgsql_funcOid_HashTable = hash_create("PLpgSQL function Oid hash", + FUNCS_PER_USER, + &ctl, + HASH_ELEM | HASH_BLOBS); +} + +static PLpgSQL_function * +plpgsql_funcOid_HashTableLookup(PLpgSQL_funcOid_hashkey *func_key) +{ + plpgsql_function_hashent *hentry; + + hentry = (plpgsql_function_hashent *) hash_search(plpgsql_funcOid_HashTable, + func_key, + HASH_FIND, + NULL); + if (hentry) + return hentry->function; + else + return NULL; +} + +static void +plpgsql_funcOid_HashTableInsert(PLpgSQL_function *function) +{ + bool found; + plpgsql_function_hashent *hentry; + PLpgSQL_funcOid_hashkey func_key; + func_key.funcOid = function->fn_oid; + + if (plpgsql_funcOid_HashTableLookup(&func_key)) + return; + + hentry = (plpgsql_function_hashent *) hash_search(plpgsql_funcOid_HashTable, + &func_key, + HASH_ENTER, + &found); + + hentry->function = function; + hentry->key.funcOid = func_key.funcOid; +} + +static void +plpgsql_funcOid_HashTableDelete(PLpgSQL_funcOid_hashkey *func_key) +{ + plpgsql_function_hashent *hentry; + + hentry = (plpgsql_function_hashent *) hash_search(plpgsql_funcOid_HashTable, + func_key, + HASH_REMOVE, + NULL); + if (hentry == NULL) + elog(WARNING, "trying to delete function that does not exist"); + + /* remove back link, which no longer points to allocated storage */ + hentry->function = NULL; + hentry->key.funcOid = InvalidOid; +} \ No newline at end of file diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index d8994538b76..7edbf974979 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -146,6 +146,7 @@ _PG_init(void) { /* Be sure we do initialization only once (should be redundant now) */ static bool inited = false; + remove_function_plancache *remove_function_plancache_ptr; if (inited) return; @@ -203,6 +204,10 @@ _PG_init(void) RegisterXactCallback(plpgsql_xact_cb, NULL); RegisterSubXactCallback(plpgsql_subxact_cb, NULL); + plpgsql_funcOid_HashTableInit(); + remove_function_plancache_ptr = (remove_function_plancache *) find_rendezvous_variable("RemoveFunctionPlanCacheById"); + *remove_function_plancache_ptr = &RemoveFunctionPlanCacheById; + /* Set up a rendezvous point with optional instrumentation plugin */ plpgsql_plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin"); diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index cead9eb7263..3ce25635009 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -1263,6 +1263,8 @@ extern PLpgSQL_condition *plpgsql_parse_err_condition(char *condname); extern void plpgsql_adddatum(PLpgSQL_datum *newdatum); extern int plpgsql_add_initdatums(int **varnos); extern void plpgsql_HashTableInit(void); +extern void plpgsql_funcOid_HashTableInit(void); +extern void RemoveFunctionPlanCacheById(Oid funcoid); /* * Functions in pl_exec.c