* so that we don't have to re-prepare simple expressions on each trip through
* a function. (We assume the case to optimize is many repetitions of a
* function within a transaction.)
+ *
+ * However, there's no value in trying to amortize simple expression setup
+ * across multiple executions of a DO block (inline code block), since there
+ * can never be any. If we use the shared EState for a DO block, the expr
+ * state trees are effectively leaked till end of transaction, and that can
+ * add up if the user keeps on submitting DO blocks. Therefore, each DO block
+ * has its own simple-expression EState, which is cleaned up at exit from
+ * plpgsql_inline_handler(). DO blocks still use the simple_econtext_stack,
+ * though, so that subxact abort cleanup does the right thing.
*/
typedef struct SimpleEcontextStackEntry
{
struct SimpleEcontextStackEntry *next; /* next stack entry up */
} SimpleEcontextStackEntry;
-static EState *simple_eval_estate = NULL;
+static EState *shared_simple_eval_estate = NULL;
static SimpleEcontextStackEntry *simple_econtext_stack = NULL;
/************************************************************
static void plpgsql_estate_setup(PLpgSQL_execstate *estate,
PLpgSQL_function *func,
- ReturnSetInfo *rsi);
+ ReturnSetInfo *rsi,
+ EState *simple_eval_estate);
static void exec_eval_cleanup(PLpgSQL_execstate *estate);
static void exec_prepare_plan(PLpgSQL_execstate *estate,
/* ----------
* plpgsql_exec_function Called by the call handler for
* function execution.
+ *
+ * This is also used to execute inline code blocks (DO blocks). The only
+ * difference that this code is aware of is that for a DO block, we want
+ * to use a private simple_eval_estate, which is created and passed in by
+ * the caller. For regular functions, pass NULL, which implies using
+ * shared_simple_eval_estate.
* ----------
*/
Datum
-plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
+plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
+ EState *simple_eval_estate)
{
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
/*
* Setup the execution state
*/
- plpgsql_estate_setup(&estate, func, (ReturnSetInfo *) fcinfo->resultinfo);
+ plpgsql_estate_setup(&estate, func, (ReturnSetInfo *) fcinfo->resultinfo,
+ simple_eval_estate);
/*
* Setup error traceback support for ereport()
/*
* Setup the execution state
*/
- plpgsql_estate_setup(&estate, func, NULL);
+ plpgsql_estate_setup(&estate, func, NULL, NULL);
/*
* Setup error traceback support for ereport()
/*
* Setup the execution state
*/
- plpgsql_estate_setup(&estate, func, NULL);
+ plpgsql_estate_setup(&estate, func, NULL, NULL);
/*
* Setup error traceback support for ereport()
static void
plpgsql_estate_setup(PLpgSQL_execstate *estate,
PLpgSQL_function *func,
- ReturnSetInfo *rsi)
+ ReturnSetInfo *rsi,
+ EState *simple_eval_estate)
{
/* this link will be restored at exit from plpgsql_call_handler */
func->cur_estate = estate;
estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
/* caller is expected to fill the datums array */
+ /* set up for use of appropriate simple-expression EState */
+ if (simple_eval_estate)
+ estate->simple_eval_estate = simple_eval_estate;
+ else
+ estate->simple_eval_estate = shared_simple_eval_estate;
+
estate->eval_tuptable = NULL;
estate->eval_processed = 0;
estate->eval_lastoid = InvalidOid;
*/
if (expr->expr_simple_lxid != curlxid)
{
- oldcontext = MemoryContextSwitchTo(simple_eval_estate->es_query_cxt);
+ oldcontext = MemoryContextSwitchTo(estate->simple_eval_estate->es_query_cxt);
expr->expr_simple_state = ExecInitExpr(expr->expr_simple_expr, NULL);
expr->expr_simple_in_use = false;
expr->expr_simple_lxid = curlxid;
/*
* plpgsql_create_econtext --- create an eval_econtext for the current function
*
- * We may need to create a new simple_eval_estate too, if there's not one
- * already for the current transaction. The EState will be cleaned up at
+ * We may need to create a new shared_simple_eval_estate too, if there's not
+ * one already for the current transaction. The EState will be cleaned up at
* transaction end.
*/
static void
* Create an EState for evaluation of simple expressions, if there's not
* one already in the current transaction. The EState is made a child of
* TopTransactionContext so it will have the right lifespan.
+ *
+ * Note that this path is never taken when executing a DO block; the
+ * required EState was already made by plpgsql_inline_handler.
*/
- if (simple_eval_estate == NULL)
+ if (estate->simple_eval_estate == NULL)
{
MemoryContext oldcontext;
+ Assert(shared_simple_eval_estate == NULL);
oldcontext = MemoryContextSwitchTo(TopTransactionContext);
- simple_eval_estate = CreateExecutorState();
+ shared_simple_eval_estate = CreateExecutorState();
+ estate->simple_eval_estate = shared_simple_eval_estate;
MemoryContextSwitchTo(oldcontext);
}
/*
* Create a child econtext for the current function.
*/
- estate->eval_econtext = CreateExprContext(simple_eval_estate);
+ estate->eval_econtext = CreateExprContext(estate->simple_eval_estate);
/*
* Make a stack entry so we can clean up the econtext at subxact end.
/* Shouldn't be any econtext stack entries left at commit */
Assert(simple_econtext_stack == NULL);
- if (simple_eval_estate)
- FreeExecutorState(simple_eval_estate);
- simple_eval_estate = NULL;
+ if (shared_simple_eval_estate)
+ FreeExecutorState(shared_simple_eval_estate);
+ shared_simple_eval_estate = NULL;
}
else if (event == XACT_EVENT_ABORT)
{
simple_econtext_stack = NULL;
- simple_eval_estate = NULL;
+ shared_simple_eval_estate = NULL;
}
}
*
* Make sure any simple-expression econtexts created in the current
* subtransaction get cleaned up. We have to do this explicitly because
- * no other code knows which child econtexts of simple_eval_estate belong
- * to which level of subxact.
+ * no other code knows which econtexts belong to which level of subxact.
*/
void
plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
retval = (Datum) 0;
}
else
- retval = plpgsql_exec_function(func, fcinfo);
+ retval = plpgsql_exec_function(func, fcinfo, NULL);
}
PG_CATCH();
{
PLpgSQL_function *func;
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
+ EState *simple_eval_estate;
Datum retval;
int rc;
flinfo.fn_oid = InvalidOid;
flinfo.fn_mcxt = CurrentMemoryContext;
- retval = plpgsql_exec_function(func, &fake_fcinfo);
+ /* Create a private EState for simple-expression execution */
+ simple_eval_estate = CreateExecutorState();
+
+ /* And run the function */
+ PG_TRY();
+ {
+ retval = plpgsql_exec_function(func, &fake_fcinfo, simple_eval_estate);
+ }
+ PG_CATCH();
+ {
+ /*
+ * We need to clean up what would otherwise be long-lived resources
+ * accumulated by the failed DO block, principally cached plans for
+ * statements (which can be flushed with plpgsql_free_function_memory)
+ * and execution trees for simple expressions, which are in the
+ * private EState.
+ *
+ * Before releasing the private EState, we must clean up any
+ * simple_econtext_stack entries pointing into it, which we can do by
+ * invoking the subxact callback. (It will be called again later if
+ * some outer control level does a subtransaction abort, but no harm
+ * is done.) We cheat a bit knowing that plpgsql_subxact_cb does not
+ * pay attention to its parentSubid argument.
+ */
+ plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
+ GetCurrentSubTransactionId(),
+ 0, NULL);
+
+ /* Clean up the private EState */
+ FreeExecutorState(simple_eval_estate);
+
+ /* 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);
+
+ /* And propagate the error */
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ /* Clean up the private EState */
+ FreeExecutorState(simple_eval_estate);
/* Function should now have no remaining use-counts ... */
func->use_count--;