diff options
Diffstat (limited to 'src/backend/executor/spi.c')
-rw-r--r-- | src/backend/executor/spi.c | 144 |
1 files changed, 128 insertions, 16 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 9b167af6608..3da2d263023 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -49,8 +49,9 @@ static int _SPI_curid = -1; static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, ParamListInfo paramLI, bool read_only); -static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan, - ParamListInfo boundParams); +static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan); + +static void _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan); static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, Snapshot snapshot, Snapshot crosscheck_snapshot, @@ -355,7 +356,7 @@ SPI_execute(const char *src, bool read_only, long tcount) plan.magic = _SPI_PLAN_MAGIC; plan.cursor_options = 0; - _SPI_prepare_plan(src, &plan, NULL); + _SPI_prepare_oneshot_plan(src, &plan); res = _SPI_execute_plan(&plan, NULL, InvalidSnapshot, InvalidSnapshot, @@ -506,7 +507,7 @@ SPI_execute_with_args(const char *src, paramLI = _SPI_convert_params(nargs, argtypes, Values, Nulls); - _SPI_prepare_plan(src, &plan, paramLI); + _SPI_prepare_oneshot_plan(src, &plan); res = _SPI_execute_plan(&plan, paramLI, InvalidSnapshot, InvalidSnapshot, @@ -547,7 +548,7 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes, plan.parserSetup = NULL; plan.parserSetupArg = NULL; - _SPI_prepare_plan(src, &plan, NULL); + _SPI_prepare_plan(src, &plan); /* copy plan to procedure context */ result = _SPI_make_plan_non_temp(&plan); @@ -584,7 +585,7 @@ SPI_prepare_params(const char *src, plan.parserSetup = parserSetup; plan.parserSetupArg = parserSetupArg; - _SPI_prepare_plan(src, &plan, NULL); + _SPI_prepare_plan(src, &plan); /* copy plan to procedure context */ result = _SPI_make_plan_non_temp(&plan); @@ -599,7 +600,8 @@ SPI_keepplan(SPIPlanPtr plan) { ListCell *lc; - if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || plan->saved) + if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || + plan->saved || plan->oneshot) return SPI_ERROR_ARGUMENT; /* @@ -1083,7 +1085,7 @@ SPI_cursor_open_with_args(const char *name, paramLI = _SPI_convert_params(nargs, argtypes, Values, Nulls); - _SPI_prepare_plan(src, &plan, paramLI); + _SPI_prepare_plan(src, &plan); /* We needn't copy the plan; SPI_cursor_open_internal will do so */ @@ -1645,10 +1647,6 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self) * * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup * and plan->parserSetupArg) must be valid, as must plan->cursor_options. - * If boundParams isn't NULL then it represents parameter values that are made - * available to the planner (as either estimates or hard values depending on - * their PARAM_FLAG_CONST marking). The boundParams had better match the - * param type information embedded in the plan! * * Results are stored into *plan (specifically, plan->plancache_list). * Note that the result data is all in CurrentMemoryContext or child contexts @@ -1657,13 +1655,12 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self) * parsing is also left in CurrentMemoryContext. */ static void -_SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams) +_SPI_prepare_plan(const char *src, SPIPlanPtr plan) { List *raw_parsetree_list; List *plancache_list; ListCell *list_item; ErrorContextCallback spierrcontext; - int cursor_options = plan->cursor_options; /* * Setup error traceback support for ereport() @@ -1726,13 +1723,80 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams) plan->nargs, plan->parserSetup, plan->parserSetupArg, - cursor_options, + plan->cursor_options, false); /* not fixed result */ plancache_list = lappend(plancache_list, plansource); } plan->plancache_list = plancache_list; + plan->oneshot = false; + + /* + * Pop the error context stack + */ + error_context_stack = spierrcontext.previous; +} + +/* + * Parse, but don't analyze, a querystring. + * + * This is a stripped-down version of _SPI_prepare_plan that only does the + * initial raw parsing. It creates "one shot" CachedPlanSources + * that still require parse analysis before execution is possible. + * + * The advantage of using the "one shot" form of CachedPlanSource is that + * we eliminate data copying and invalidation overhead. Postponing parse + * analysis also prevents issues if some of the raw parsetrees are DDL + * commands that affect validity of later parsetrees. Both of these + * attributes are good things for SPI_execute() and similar cases. + * + * Results are stored into *plan (specifically, plan->plancache_list). + * Note that the result data is all in CurrentMemoryContext or child contexts + * thereof; in practice this means it is in the SPI executor context, and + * what we are creating is a "temporary" SPIPlan. Cruft generated during + * parsing is also left in CurrentMemoryContext. + */ +static void +_SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan) +{ + List *raw_parsetree_list; + List *plancache_list; + ListCell *list_item; + ErrorContextCallback spierrcontext; + + /* + * Setup error traceback support for ereport() + */ + spierrcontext.callback = _SPI_error_callback; + spierrcontext.arg = (void *) src; + spierrcontext.previous = error_context_stack; + error_context_stack = &spierrcontext; + + /* + * Parse the request string into a list of raw parse trees. + */ + raw_parsetree_list = pg_parse_query(src); + + /* + * Construct plancache entries, but don't do parse analysis yet. + */ + plancache_list = NIL; + + foreach(list_item, raw_parsetree_list) + { + Node *parsetree = (Node *) lfirst(list_item); + CachedPlanSource *plansource; + + plansource = CreateOneShotCachedPlan(parsetree, + src, + CreateCommandTag(parsetree)); + + plancache_list = lappend(plancache_list, plansource); + } + + plan->plancache_list = plancache_list; + plan->oneshot = true; /* * Pop the error context stack @@ -1770,7 +1834,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, * Setup error traceback support for ereport() */ spierrcontext.callback = _SPI_error_callback; - spierrcontext.arg = NULL; + spierrcontext.arg = NULL; /* we'll fill this below */ spierrcontext.previous = error_context_stack; error_context_stack = &spierrcontext; @@ -1817,6 +1881,47 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, spierrcontext.arg = (void *) plansource->query_string; /* + * If this is a one-shot plan, we still need to do parse analysis. + */ + if (plan->oneshot) + { + Node *parsetree = plansource->raw_parse_tree; + const char *src = plansource->query_string; + List *stmt_list; + + /* + * Parameter datatypes are driven by parserSetup hook if provided, + * otherwise we use the fixed parameter list. + */ + if (plan->parserSetup != NULL) + { + Assert(plan->nargs == 0); + stmt_list = pg_analyze_and_rewrite_params(parsetree, + src, + plan->parserSetup, + plan->parserSetupArg); + } + else + { + stmt_list = pg_analyze_and_rewrite(parsetree, + src, + plan->argtypes, + plan->nargs); + } + + /* Finish filling in the CachedPlanSource */ + CompleteCachedPlan(plansource, + stmt_list, + NULL, + plan->argtypes, + plan->nargs, + plan->parserSetup, + plan->parserSetupArg, + plan->cursor_options, + false); /* not fixed result */ + } + + /* * Replan if needed, and increment plan refcount. If it's a saved * plan, the refcount must be backed by the CurrentResourceOwner. */ @@ -2313,6 +2418,8 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan) /* Assert the input is a temporary SPIPlan */ Assert(plan->magic == _SPI_PLAN_MAGIC); Assert(plan->plancxt == NULL); + /* One-shot plans can't be saved */ + Assert(!plan->oneshot); /* * Create a memory context for the plan, underneath the procedure context. @@ -2330,6 +2437,7 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan) newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan)); newplan->magic = _SPI_PLAN_MAGIC; newplan->saved = false; + newplan->oneshot = false; newplan->plancache_list = NIL; newplan->plancxt = plancxt; newplan->cursor_options = plan->cursor_options; @@ -2379,6 +2487,9 @@ _SPI_save_plan(SPIPlanPtr plan) MemoryContext oldcxt; ListCell *lc; + /* One-shot plans can't be saved */ + Assert(!plan->oneshot); + /* * Create a memory context for the plan. We don't expect the plan to be * very large, so use smaller-than-default alloc parameters. It's a @@ -2395,6 +2506,7 @@ _SPI_save_plan(SPIPlanPtr plan) newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan)); newplan->magic = _SPI_PLAN_MAGIC; newplan->saved = false; + newplan->oneshot = false; newplan->plancache_list = NIL; newplan->plancxt = plancxt; newplan->cursor_options = plan->cursor_options; |