@ -102,6 +102,16 @@ typedef struct SimpleEcontextStackEntry
static EState * shared_simple_eval_estate = NULL ;
static SimpleEcontextStackEntry * simple_econtext_stack = NULL ;
/*
* In addition to the shared simple - eval EState , we have a shared resource
* owner that holds refcounts on the CachedPlans for any " simple " expressions
* we have evaluated in the current transaction . This allows us to avoid
* continually grabbing and releasing a plan refcount when a simple expression
* is used over and over . ( DO blocks use their own resowner , in exactly the
* same way described above for shared_simple_eval_estate . )
*/
static ResourceOwner shared_simple_eval_resowner = NULL ;
/*
* Memory management within a plpgsql function generally works with three
* contexts :
@ -321,7 +331,8 @@ static int exec_stmt_set(PLpgSQL_execstate *estate,
static void plpgsql_estate_setup ( PLpgSQL_execstate * estate ,
PLpgSQL_function * func ,
ReturnSetInfo * rsi ,
EState * simple_eval_estate ) ;
EState * simple_eval_estate ,
ResourceOwner simple_eval_resowner ) ;
static void exec_eval_cleanup ( PLpgSQL_execstate * estate ) ;
static void exec_prepare_plan ( PLpgSQL_execstate * estate ,
@ -447,16 +458,19 @@ static char *format_preparedparamsdata(PLpgSQL_execstate *estate,
*
* 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 . ( When using a private simple_eval_estate ,
* to use a private simple_eval_estate and a private simple_eval_resowner ,
* which are created and passed in by the caller . For regular functions ,
* pass NULL , which implies using shared_simple_eval_estate and
* shared_simple_eval_resowner . ( When using a private simple_eval_estate ,
* we must also use a private cast hashtable , but that ' s taken care of
* within plpgsql_estate_setup . )
* - - - - - - - - - -
*/
Datum
plpgsql_exec_function ( PLpgSQL_function * func , FunctionCallInfo fcinfo ,
EState * simple_eval_estate , bool atomic )
EState * simple_eval_estate ,
ResourceOwner simple_eval_resowner ,
bool atomic )
{
PLpgSQL_execstate estate ;
ErrorContextCallback plerrcontext ;
@ -467,7 +481,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
* Setup the execution state
*/
plpgsql_estate_setup ( & estate , func , ( ReturnSetInfo * ) fcinfo - > resultinfo ,
simple_eval_estate ) ;
simple_eval_estate , simple_eval_resowner ) ;
estate . atomic = atomic ;
/*
@ -904,7 +918,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
/*
* Setup the execution state
*/
plpgsql_estate_setup ( & estate , func , NULL , NULL ) ;
plpgsql_estate_setup ( & estate , func , NULL , NULL , NULL ) ;
estate . trigdata = trigdata ;
/*
@ -1142,7 +1156,7 @@ plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
/*
* Setup the execution state
*/
plpgsql_estate_setup ( & estate , func , NULL , NULL ) ;
plpgsql_estate_setup ( & estate , func , NULL , NULL , NULL ) ;
estate . evtrigdata = trigdata ;
/*
@ -2326,6 +2340,7 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
* simple - expression infrastructure .
*/
estate - > simple_eval_estate = NULL ;
estate - > simple_eval_resowner = NULL ;
plpgsql_create_econtext ( estate ) ;
}
@ -3881,7 +3896,8 @@ static void
plpgsql_estate_setup ( PLpgSQL_execstate * estate ,
PLpgSQL_function * func ,
ReturnSetInfo * rsi ,
EState * simple_eval_estate )
EState * simple_eval_estate ,
ResourceOwner simple_eval_resowner )
{
HASHCTL ctl ;
@ -3972,6 +3988,11 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
estate - > cast_hash = shared_cast_hash ;
estate - > cast_hash_context = shared_cast_context ;
}
/* likewise for the simple-expression resource owner */
if ( simple_eval_resowner )
estate - > simple_eval_resowner = simple_eval_resowner ;
else
estate - > simple_eval_resowner = shared_simple_eval_resowner ;
/*
* We start with no stmt_mcontext ; one will be created only if needed .
@ -4836,6 +4857,7 @@ exec_stmt_commit(PLpgSQL_execstate *estate, PLpgSQL_stmt_commit *stmt)
* data structures are gone .
*/
estate - > simple_eval_estate = NULL ;
estate - > simple_eval_resowner = NULL ;
plpgsql_create_econtext ( estate ) ;
return PLPGSQL_RC_OK ;
@ -4862,6 +4884,7 @@ exec_stmt_rollback(PLpgSQL_execstate *estate, PLpgSQL_stmt_rollback *stmt)
* data structures are gone .
*/
estate - > simple_eval_estate = NULL ;
estate - > simple_eval_resowner = NULL ;
plpgsql_create_econtext ( estate ) ;
return PLPGSQL_RC_OK ;
@ -6074,8 +6097,6 @@ loop_exit:
* someone might redefine a SQL function that had been inlined into the simple
* expression . That cannot cause a simple expression to become non - simple ( or
* vice versa ) , but we do have to handle replacing the expression tree .
* Fortunately it ' s normally inexpensive to call SPI_plan_get_cached_plan for
* a simple expression .
*
* Note : if pass - by - reference , the result is in the eval_mcontext .
* It will be freed when exec_eval_cleanup is done .
@ -6091,7 +6112,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
{
ExprContext * econtext = estate - > eval_econtext ;
LocalTransactionId curlxid = MyProc - > lxid ;
CachedPlan * cplan ;
ParamListInfo paramLI ;
void * save_setup_arg ;
bool need_snapshot ;
MemoryContext oldcontext ;
@ -6105,29 +6126,92 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
/*
* If expression is in use in current xact , don ' t touch it .
*/
if ( expr - > expr_simple_in_use & & expr - > expr_simple_lxid = = curlxid )
if ( unlikely ( expr - > expr_simple_in_use ) & &
expr - > expr_simple_lxid = = curlxid )
return false ;
/*
* Revalidate cached plan , so that we will notice if it became stale . ( We
* need to hold a refcount while using the plan , anyway . ) If replanning
* is needed , do that work in the eval_mcontext .
* Check to see if the cached plan has been invalidated . If not , and this
* is the first use in the current transaction , save a plan refcount in
* the simple - expression resowner .
*/
oldcontext = MemoryContextSwitchTo ( get_eval_mcontext ( estate ) ) ;
cplan = SPI_plan_get_cached_plan ( expr - > plan ) ;
MemoryContextSwitchTo ( oldcontext ) ;
if ( likely ( CachedPlanIsSimplyValid ( expr - > expr_simple_plansource ,
expr - > expr_simple_plan ,
( expr - > expr_simple_plan_lxid ! = curlxid ?
estate - > simple_eval_resowner : NULL ) ) ) )
{
/*
* It ' s still good , so just remember that we have a refcount on the
* plan in the current transaction . ( If we already had one , this
* assignment is a no - op . )
*/
expr - > expr_simple_plan_lxid = curlxid ;
}
else
{
/* Need to replan */
CachedPlan * cplan ;
/*
* We can ' t get a failure here , because the number of CachedPlanSources in
* the SPI plan can ' t change from what exec_simple_check_plan saw ; it ' s a
* property of the raw parsetree generated from the query text .
*/
Assert ( cplan ! = NULL ) ;
/*
* If we have a valid refcount on some previous version of the plan ,
* release it , so we don ' t leak plans intra - transaction .
*/
if ( expr - > expr_simple_plan_lxid = = curlxid )
{
ResourceOwner saveResourceOwner = CurrentResourceOwner ;
/* If it got replanned, update our copy of the simple expression */
if ( cplan - > generation ! = expr - > expr_simple_generation )
{
CurrentResourceOwner = estate - > simple_eval_resowner ;
ReleaseCachedPlan ( expr - > expr_simple_plan , true ) ;
CurrentResourceOwner = saveResourceOwner ;
expr - > expr_simple_plan = NULL ;
expr - > expr_simple_plan_lxid = InvalidLocalTransactionId ;
}
/* Do the replanning work in the eval_mcontext */
oldcontext = MemoryContextSwitchTo ( get_eval_mcontext ( estate ) ) ;
cplan = SPI_plan_get_cached_plan ( expr - > plan ) ;
MemoryContextSwitchTo ( oldcontext ) ;
/*
* We can ' t get a failure here , because the number of
* CachedPlanSources in the SPI plan can ' t change from what
* exec_simple_check_plan saw ; it ' s a property of the raw parsetree
* generated from the query text .
*/
Assert ( cplan ! = NULL ) ;
/*
* These tests probably can ' t fail either , but if they do , cope by
* declaring the plan to be non - simple . On success , we ' ll acquire a
* refcount on the new plan , stored in simple_eval_resowner .
*/
if ( CachedPlanAllowsSimpleValidityCheck ( expr - > expr_simple_plansource ,
cplan ) & &
CachedPlanIsSimplyValid ( expr - > expr_simple_plansource , cplan ,
estate - > simple_eval_resowner ) )
{
/* Remember that we have the refcount */
expr - > expr_simple_plan = cplan ;
expr - > expr_simple_plan_lxid = curlxid ;
}
else
{
/* Release SPI_plan_get_cached_plan's refcount */
ReleaseCachedPlan ( cplan , true ) ;
/* Mark expression as non-simple, and fail */
expr - > expr_simple_expr = NULL ;
return false ;
}
/*
* SPI_plan_get_cached_plan acquired a plan refcount stored in the
* active resowner . We don ' t need that anymore , so release it .
*/
ReleaseCachedPlan ( cplan , true ) ;
/* Extract desired scalar expression from cached plan */
exec_save_simple_expr ( expr , cplan ) ;
/* better recheck r/w safety, as it could change due to inlining */
if ( expr - > rwparam > = 0 )
exec_check_rw_parameter ( expr , expr - > rwparam ) ;
@ -6143,16 +6227,24 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
* Set up ParamListInfo to pass to executor . For safety , save and restore
* estate - > paramLI - > parserSetupArg around our use of the param list .
*/
save_setup_arg = estate - > paramLI - > parserSetupArg ;
paramLI = estate - > paramLI ;
save_setup_arg = paramLI - > parserSetupArg ;
econtext - > ecxt_param_list_info = setup_param_list ( estate , expr ) ;
/*
* We can skip using setup_param_list ( ) in favor of just doing this
* unconditionally , because there ' s no need for the optimization of
* possibly setting ecxt_param_list_info to NULL ; we ' ve already forced use
* of a generic plan .
*/
paramLI - > parserSetupArg = ( void * ) expr ;
econtext - > ecxt_param_list_info = paramLI ;
/*
* Prepare the expression for execution , if it ' s not been done already in
* the current transaction . ( This will be forced to happen if we called
* exec_save_simple_expr above . )
*/
if ( expr - > expr_simple_lxid ! = curlxid )
if ( unlikely ( expr - > expr_simple_lxid ! = curlxid ) )
{
oldcontext = MemoryContextSwitchTo ( estate - > simple_eval_estate - > es_query_cxt ) ;
expr - > expr_simple_state =
@ -6200,18 +6292,13 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
econtext - > ecxt_param_list_info = NULL ;
estate - > paramLI - > parserSetupArg = save_setup_arg ;
paramLI - > parserSetupArg = save_setup_arg ;
if ( need_snapshot )
PopActiveSnapshot ( ) ;
MemoryContextSwitchTo ( oldcontext ) ;
/*
* Now we can release our refcount on the cached plan .
*/
ReleaseCachedPlan ( cplan , true ) ;
/*
* That ' s it .
*/
@ -7999,10 +8086,35 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
/* Can't fail, because we checked for a single CachedPlanSource above */
Assert ( cplan ! = NULL ) ;
/* Share the remaining work with replan code path */
exec_save_simple_expr ( expr , cplan ) ;
/*
* Verify that plancache . c thinks the plan is simple enough to use
* CachedPlanIsSimplyValid . Given the restrictions above , it ' s unlikely
* that this could fail , but if it does , just treat plan as not simple .
*/
if ( CachedPlanAllowsSimpleValidityCheck ( plansource , cplan ) )
{
/*
* OK , use CachedPlanIsSimplyValid to save a refcount on the plan in
* the simple - expression resowner . This shouldn ' t fail either , but if
* somehow it does , again we can cope by treating plan as not simple .
*/
if ( CachedPlanIsSimplyValid ( plansource , cplan ,
estate - > simple_eval_resowner ) )
{
/* Remember that we have the refcount */
expr - > expr_simple_plansource = plansource ;
expr - > expr_simple_plan = cplan ;
expr - > expr_simple_plan_lxid = MyProc - > lxid ;
/* Share the remaining work with the replan code path */
exec_save_simple_expr ( expr , cplan ) ;
}
}
/* Release our plan refcount */
/*
* Release the plan refcount obtained by SPI_plan_get_cached_plan . ( This
* refcount is held by the wrong resowner , so we can ' t just repurpose it . )
*/
ReleaseCachedPlan ( cplan , true ) ;
}
@ -8075,7 +8187,6 @@ exec_save_simple_expr(PLpgSQL_expr *expr, CachedPlan *cplan)
* current transaction " .
*/
expr - > expr_simple_expr = tle_expr ;
expr - > expr_simple_generation = cplan - > generation ;
expr - > expr_simple_state = NULL ;
expr - > expr_simple_in_use = false ;
expr - > expr_simple_lxid = InvalidLocalTransactionId ;
@ -8211,7 +8322,7 @@ exec_set_found(PLpgSQL_execstate *estate, bool state)
*
* 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 .
* transaction end . Ditto for shared_simple_eval_resowner .
*/
static void
plpgsql_create_econtext ( PLpgSQL_execstate * estate )
@ -8244,6 +8355,18 @@ plpgsql_create_econtext(PLpgSQL_execstate *estate)
estate - > simple_eval_estate = shared_simple_eval_estate ;
}
/*
* Likewise for the simple - expression resource owner .
*/
if ( estate - > simple_eval_resowner = = NULL )
{
if ( shared_simple_eval_resowner = = NULL )
shared_simple_eval_resowner =
ResourceOwnerCreate ( TopTransactionResourceOwner ,
" PL/pgSQL simple expressions " ) ;
estate - > simple_eval_resowner = shared_simple_eval_resowner ;
}
/*
* Create a child econtext for the current function .
*/
@ -8290,16 +8413,20 @@ plpgsql_destroy_econtext(PLpgSQL_execstate *estate)
* plpgsql_xact_cb - - - post - transaction - commit - or - abort cleanup
*
* If a simple - expression EState was created in the current transaction ,
* it has to be cleaned up .
* it has to be cleaned up . The same for the simple - expression resowner .
*/
void
plpgsql_xact_cb ( XactEvent event , void * arg )
{
/*
* If we are doing a clean transaction shutdown , free the EState ( so that
* any remaining resources will be released correctly ) . In an abort , we
* expect the regular abort recovery procedures to release everything of
* interest .
* If we are doing a clean transaction shutdown , free the EState and tell
* the resowner to release whatever plancache references it has , so that
* all remaining resources will be released correctly . ( We don ' t need to
* actually delete the resowner here ; deletion of the
* TopTransactionResourceOwner will take care of that . )
*
* In an abort , we expect the regular abort recovery procedures to release
* everything of interest , so just clear our pointers .
*/
if ( event = = XACT_EVENT_COMMIT | |
event = = XACT_EVENT_PARALLEL_COMMIT | |
@ -8310,12 +8437,16 @@ plpgsql_xact_cb(XactEvent event, void *arg)
if ( shared_simple_eval_estate )
FreeExecutorState ( shared_simple_eval_estate ) ;
shared_simple_eval_estate = NULL ;
if ( shared_simple_eval_resowner )
ResourceOwnerReleaseAllPlanCacheRefs ( shared_simple_eval_resowner ) ;
shared_simple_eval_resowner = NULL ;
}
else if ( event = = XACT_EVENT_ABORT | |
event = = XACT_EVENT_PARALLEL_ABORT )
{
simple_econtext_stack = NULL ;
shared_simple_eval_estate = NULL ;
shared_simple_eval_resowner = NULL ;
}
}