diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index a928e2f..b2139d6 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -2368,12 +2368,9 @@ delete_function(PLpgSQL_function *func) /* remove function from hash table (might be done already) */ plpgsql_HashTableDelete(func); - /* release the function's storage if safe and not done already */ - if (func->use_count == 0 && func->fn_cxt) - { - MemoryContextDelete(func->fn_cxt); - func->fn_cxt = NULL; - } + /* release the function's storage if safe */ + if (func->use_count) + plpgsql_free_function_memory(func); } /* exported so we can call it from plpgsql_init() */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 1f4d5ac..e353374 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -2919,6 +2919,7 @@ static void exec_prepare_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr, int cursorOptions) { + MemoryContext oldcxt; SPIPlanPtr plan; /* @@ -2956,6 +2957,11 @@ exec_prepare_plan(PLpgSQL_execstate *estate, expr->plan = SPI_saveplan(plan); SPI_freeplan(plan); exec_simple_check_plan(expr); + + /* Remember this plan so that it can be freed along with the function */ + oldcxt = MemoryContextSwitchTo(expr->func->fn_cxt); + expr->func->cached_plans = lappend(expr->func->cached_plans, expr->plan); + MemoryContextSwitchTo(oldcxt); } diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index f13e4c3..14c5e08 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -15,6 +15,8 @@ #include "plpgsql.h" +#include "utils/memutils.h" + /* ---------- * Local variables for namespace handling @@ -264,6 +266,37 @@ plpgsql_stmt_typename(PLpgSQL_stmt *stmt) } +/* + * Release memory when a PL/pgSQL function is no longer needed + */ +void +plpgsql_free_function_memory(PLpgSQL_function *func) +{ + ListCell *lc; + + /* Better not call this on an in-use function */ + Assert(func->use_count == 0); + + /* Release cached plans belonging to the function */ + foreach(lc, func->cached_plans) + { + SPI_freeplan(lfirst(lc)); + } + list_free(func->cached_plans); + func->cached_plans = NIL; + + /* + * And finally, release all memory except the PLpgSQL_function struct + * itself (which has to be kept around because there may be multiple + * fn_extra pointers to it). + */ + if (func->fn_cxt) + { + MemoryContextDelete(func->fn_cxt); + func->fn_cxt = NULL; + } +} + /********************************************************************** * Debug functions for analyzing the compiled code **********************************************************************/ diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 8f7080e..2389849 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -172,6 +172,9 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS) /* Compile the anonymous code block */ func = plpgsql_compile_inline(codeblock->source_text); + /* Mark the function as busy, just pro forma */ + func->use_count++; + /* * Set up a fake fcinfo with just enough info to satisfy * plpgsql_exec_function(). In particular note that this sets things up @@ -185,6 +188,13 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS) retval = plpgsql_exec_function(func, &fake_fcinfo); + /* Function should now have no remaining use-counts ... */ + func->use_count--; + Assert(func->use_count == 0); + + /* ... so we can free subsidiary storage */ + plpgsql_free_function_memory(func); + /* * Disconnect from SPI manager */ diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 7015379..9efa982 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -693,6 +693,8 @@ typedef struct PLpgSQL_function /* these fields change when the function is used */ struct PLpgSQL_execstate *cur_estate; unsigned long use_count; + + List *cached_plans; } PLpgSQL_function; @@ -918,6 +920,7 @@ extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur, * ---------- */ extern const char *plpgsql_stmt_typename(PLpgSQL_stmt *stmt); +extern void plpgsql_free_function_memory(PLpgSQL_function *func); extern void plpgsql_dumptree(PLpgSQL_function *func); /* ----------