@ -181,6 +181,7 @@ CreateCachedPlan(Node *raw_parse_tree,
plansource - > invalItems = NIL ;
plansource - > query_context = NULL ;
plansource - > gplan = NULL ;
plansource - > is_oneshot = false ;
plansource - > is_complete = false ;
plansource - > is_saved = false ;
plansource - > is_valid = false ;
@ -195,6 +196,69 @@ CreateCachedPlan(Node *raw_parse_tree,
return plansource ;
}
/*
* CreateOneShotCachedPlan : initially create a one - shot plan cache entry .
*
* This variant of CreateCachedPlan creates a plan cache entry that is meant
* to be used only once . No data copying occurs : all data structures remain
* in the caller ' s memory context ( which typically should get cleared after
* completing execution ) . The CachedPlanSource struct itself is also created
* in that context .
*
* A one - shot plan cannot be saved or copied , since we make no effort to
* preserve the raw parse tree unmodified . There is also no support for
* invalidation , so plan use must be completed in the current transaction ,
* and DDL that might invalidate the querytree_list must be avoided as well .
*
* raw_parse_tree : output of raw_parser ( )
* query_string : original query text
* commandTag : compile - time - constant tag for query , or NULL if empty query
*/
CachedPlanSource *
CreateOneShotCachedPlan ( Node * raw_parse_tree ,
const char * query_string ,
const char * commandTag )
{
CachedPlanSource * plansource ;
Assert ( query_string ! = NULL ) ; /* required as of 8.4 */
/*
* Create and fill the CachedPlanSource struct within the caller ' s memory
* context . Most fields are just left empty for the moment .
*/
plansource = ( CachedPlanSource * ) palloc0 ( sizeof ( CachedPlanSource ) ) ;
plansource - > magic = CACHEDPLANSOURCE_MAGIC ;
plansource - > raw_parse_tree = raw_parse_tree ;
plansource - > query_string = query_string ;
plansource - > commandTag = commandTag ;
plansource - > param_types = NULL ;
plansource - > num_params = 0 ;
plansource - > parserSetup = NULL ;
plansource - > parserSetupArg = NULL ;
plansource - > cursor_options = 0 ;
plansource - > fixed_result = false ;
plansource - > resultDesc = NULL ;
plansource - > search_path = NULL ;
plansource - > context = CurrentMemoryContext ;
plansource - > query_list = NIL ;
plansource - > relationOids = NIL ;
plansource - > invalItems = NIL ;
plansource - > query_context = NULL ;
plansource - > gplan = NULL ;
plansource - > is_oneshot = true ;
plansource - > is_complete = false ;
plansource - > is_saved = false ;
plansource - > is_valid = false ;
plansource - > generation = 0 ;
plansource - > next_saved = NULL ;
plansource - > generic_cost = - 1 ;
plansource - > total_custom_cost = 0 ;
plansource - > num_custom_plans = 0 ;
return plansource ;
}
/*
* CompleteCachedPlan : second step of creating a plan cache entry .
*
@ -222,6 +286,10 @@ CreateCachedPlan(Node *raw_parse_tree,
* option , it is caller ' s responsibility that the referenced data remains
* valid for as long as the CachedPlanSource exists .
*
* If the CachedPlanSource is a " oneshot " plan , then no querytree copying
* occurs at all , and querytree_context is ignored ; it is caller ' s
* responsibility that the passed querytree_list is sufficiently long - lived .
*
* plansource : structure returned by CreateCachedPlan
* querytree_list : analyzed - and - rewritten form of query ( list of Query nodes )
* querytree_context : memory context containing querytree_list ,
@ -254,9 +322,15 @@ CompleteCachedPlan(CachedPlanSource *plansource,
/*
* If caller supplied a querytree_context , reparent it underneath the
* CachedPlanSource ' s context ; otherwise , create a suitable context and
* copy the querytree_list into it .
* copy the querytree_list into it . But no data copying should be done
* for one - shot plans ; for those , assume the passed querytree_list is
* sufficiently long - lived .
*/
if ( querytree_context ! = NULL )
if ( plansource - > is_oneshot )
{
querytree_context = CurrentMemoryContext ;
}
else if ( querytree_context ! = NULL )
{
MemoryContextSetParent ( querytree_context , source_context ) ;
MemoryContextSwitchTo ( querytree_context ) ;
@ -279,11 +353,12 @@ CompleteCachedPlan(CachedPlanSource *plansource,
/*
* Use the planner machinery to extract dependencies . Data is saved in
* query_context . ( We assume that not a lot of extra cruft is created by
* this call . )
* this call . ) We can skip this for one - shot plans .
*/
extract_query_dependencies ( ( Node * ) querytree_list ,
& plansource - > relationOids ,
& plansource - > invalItems ) ;
if ( ! plansource - > is_oneshot )
extract_query_dependencies ( ( Node * ) querytree_list ,
& plansource - > relationOids ,
& plansource - > invalItems ) ;
/*
* Save the final parameter types ( or other parameter specification data )
@ -326,7 +401,8 @@ CompleteCachedPlan(CachedPlanSource *plansource,
* it to the list of cached plans that are checked for invalidation when an
* sinval event occurs .
*
* This is guaranteed not to throw error ; callers typically depend on that
* This is guaranteed not to throw error , except for the caller - error case
* of trying to save a one - shot plan . Callers typically depend on that
* since this is called just before or just after adding a pointer to the
* CachedPlanSource to some permanent data structure of their own . Up until
* this is done , a CachedPlanSource is just transient data that will go away
@ -340,6 +416,10 @@ SaveCachedPlan(CachedPlanSource *plansource)
Assert ( plansource - > is_complete ) ;
Assert ( ! plansource - > is_saved ) ;
/* This seems worth a real test, though */
if ( plansource - > is_oneshot )
elog ( ERROR , " cannot save one-shot cached plan " ) ;
/*
* In typical use , this function would be called before generating any
* plans from the CachedPlanSource . If there is a generic plan , moving it
@ -402,11 +482,15 @@ DropCachedPlan(CachedPlanSource *plansource)
/* Decrement generic CachePlan's refcount and drop if no longer needed */
ReleaseGenericPlan ( plansource ) ;
/* Mark it no longer valid */
plansource - > magic = 0 ;
/*
* Remove the CachedPlanSource and all subsidiary data ( including the
* query_context if any ) .
* query_context if any ) . But if it ' s a one - shot we can ' t free anything .
*/
MemoryContextDelete ( plansource - > context ) ;
if ( ! plansource - > is_oneshot )
MemoryContextDelete ( plansource - > context ) ;
}
/*
@ -451,6 +535,17 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
MemoryContext querytree_context ;
MemoryContext oldcxt ;
/*
* For one - shot plans , we do not support revalidation checking ; it ' s
* assumed the query is parsed , planned , and executed in one transaction ,
* so that no lock re - acquisition is necessary .
*/
if ( plansource - > is_oneshot )
{
Assert ( plansource - > is_valid ) ;
return NIL ;
}
/*
* If the query is currently valid , acquire locks on the referenced
* objects ; then check again . We need to do it this way to cover the race
@ -649,6 +744,8 @@ CheckCachedPlan(CachedPlanSource *plansource)
return false ;
Assert ( plan - > magic = = CACHEDPLAN_MAGIC ) ;
/* Generic plans are never one-shot */
Assert ( ! plan - > is_oneshot ) ;
/*
* If it appears valid , acquire locks and recheck ; this is much the same
@ -708,7 +805,8 @@ CheckCachedPlan(CachedPlanSource *plansource)
* hint rather than a hard constant .
*
* Planning work is done in the caller ' s memory context . The finished plan
* is in a child memory context , which typically should get reparented .
* is in a child memory context , which typically should get reparented
* ( unless this is a one - shot plan , in which case we don ' t copy the plan ) .
*/
static CachedPlan *
BuildCachedPlan ( CachedPlanSource * plansource , List * qlist ,
@ -719,7 +817,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
bool snapshot_set ;
bool spi_pushed ;
MemoryContext plan_context ;
MemoryContext oldcxt ;
MemoryContext oldcxt = CurrentMemoryContext ;
/*
* Normally the querytree should be valid already , but if it ' s not ,
@ -739,10 +837,16 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
/*
* If we don ' t already have a copy of the querytree list that can be
* scribbled on by the planner , make one .
* scribbled on by the planner , make one . For a one - shot plan , we assume
* it ' s okay to scribble on the original query_list .
*/
if ( qlist = = NIL )
qlist = ( List * ) copyObject ( plansource - > query_list ) ;
{
if ( ! plansource - > is_oneshot )
qlist = ( List * ) copyObject ( plansource - > query_list ) ;
else
qlist = plansource - > query_list ;
}
/*
* Restore the search_path that was in use when the plan was made . See
@ -794,22 +898,29 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
PopOverrideSearchPath ( ) ;
/*
* Make a dedicated memory context for the CachedPlan and its subsidiary
* data . It ' s probably not going to be large , but just in case , use the
* default maxsize parameter . It ' s transient for the moment .
* Normally we make a dedicated memory context for the CachedPlan and its
* subsidiary data . ( It ' s probably not going to be large , but just in
* case , use the default maxsize parameter . It ' s transient for the
* moment . ) But for a one - shot plan , we just leave it in the caller ' s
* memory context .
*/
plan_context = AllocSetContextCreate ( CurrentMemoryContext ,
" CachedPlan " ,
ALLOCSET_SMALL_MINSIZE ,
ALLOCSET_SMALL_INITSIZE ,
ALLOCSET_DEFAULT_MAXSIZE ) ;
if ( ! plansource - > is_oneshot )
{
plan_context = AllocSetContextCreate ( CurrentMemoryContext ,
" CachedPlan " ,
ALLOCSET_SMALL_MINSIZE ,
ALLOCSET_SMALL_INITSIZE ,
ALLOCSET_DEFAULT_MAXSIZE ) ;
/*
* Copy plan into the new context .
*/
oldcxt = MemoryContextSwitchTo ( plan_context ) ;
/*
* Copy plan into the new context .
*/
MemoryContextSwitchTo ( plan_context ) ;
plist = ( List * ) copyObject ( plist ) ;
plist = ( List * ) copyObject ( plist ) ;
}
else
plan_context = CurrentMemoryContext ;
/*
* Create and fill the CachedPlan struct within the new context .
@ -826,6 +937,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
plan - > saved_xmin = InvalidTransactionId ;
plan - > refcount = 0 ;
plan - > context = plan_context ;
plan - > is_oneshot = plansource - > is_oneshot ;
plan - > is_saved = false ;
plan - > is_valid = true ;
@ -847,7 +959,11 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
{
double avg_custom_cost ;
/* Never any point in a custom plan if there's no parameters */
/* One-shot plans will always be considered custom */
if ( plansource - > is_oneshot )
return true ;
/* Otherwise, never any point in a custom plan if there's no parameters */
if ( boundParams = = NULL )
return false ;
@ -1049,7 +1165,14 @@ ReleaseCachedPlan(CachedPlan *plan, bool useResOwner)
Assert ( plan - > refcount > 0 ) ;
plan - > refcount - - ;
if ( plan - > refcount = = 0 )
MemoryContextDelete ( plan - > context ) ;
{
/* Mark it no longer valid */
plan - > magic = 0 ;
/* One-shot plans do not own their context, so we can't free them */
if ( ! plan - > is_oneshot )
MemoryContextDelete ( plan - > context ) ;
}
}
/*
@ -1066,9 +1189,11 @@ CachedPlanSetParentContext(CachedPlanSource *plansource,
Assert ( plansource - > magic = = CACHEDPLANSOURCE_MAGIC ) ;
Assert ( plansource - > is_complete ) ;
/* This seems worth a real test , though */
/* These seem worth real tests , though */
if ( plansource - > is_saved )
elog ( ERROR , " cannot move a saved cached plan to another context " ) ;
if ( plansource - > is_oneshot )
elog ( ERROR , " cannot move a one-shot cached plan to another context " ) ;
/* OK, let the caller keep the plan where he wishes */
MemoryContextSetParent ( plansource - > context , newcontext ) ;
@ -1105,6 +1230,13 @@ CopyCachedPlan(CachedPlanSource *plansource)
Assert ( plansource - > magic = = CACHEDPLANSOURCE_MAGIC ) ;
Assert ( plansource - > is_complete ) ;
/*
* One - shot plans can ' t be copied , because we haven ' t taken care that
* parsing / planning didn ' t scribble on the raw parse tree or querytrees .
*/
if ( plansource - > is_oneshot )
elog ( ERROR , " cannot copy a one-shot cached plan " ) ;
source_context = AllocSetContextCreate ( CurrentMemoryContext ,
" CachedPlanSource " ,
ALLOCSET_SMALL_MINSIZE ,
@ -1152,6 +1284,7 @@ CopyCachedPlan(CachedPlanSource *plansource)
newsource - > gplan = NULL ;
newsource - > is_oneshot = false ;
newsource - > is_complete = true ;
newsource - > is_saved = false ;
newsource - > is_valid = plansource - > is_valid ;