diff options
Diffstat (limited to 'src/backend/executor/spi.c')
-rw-r--r-- | src/backend/executor/spi.c | 242 |
1 files changed, 169 insertions, 73 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index e6bb04bc8a8..fcea0a1e623 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.210 2009/10/10 01:43:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.211 2009/11/04 22:26:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,8 +45,7 @@ static int _SPI_connected = -1; static int _SPI_curid = -1; static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, - Datum *Values, const char *Nulls, - bool read_only, int pflags); + ParamListInfo paramLI, bool read_only); static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams); @@ -407,6 +406,28 @@ SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount) return SPI_execute_plan(plan, Values, Nulls, false, tcount); } +/* Execute a previously prepared plan */ +int +SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params, + bool read_only, long tcount) +{ + int res; + + if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0) + return SPI_ERROR_ARGUMENT; + + res = _SPI_begin_call(true); + if (res < 0) + return res; + + res = _SPI_execute_plan(plan, params, + InvalidSnapshot, InvalidSnapshot, + read_only, true, tcount); + + _SPI_end_call(true); + return res; +} + /* * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow * the caller to specify exactly which snapshots to use, which will be @@ -483,6 +504,8 @@ SPI_execute_with_args(const char *src, plan.cursor_options = 0; plan.nargs = nargs; plan.argtypes = argtypes; + plan.parserSetup = NULL; + plan.parserSetupArg = NULL; paramLI = _SPI_convert_params(nargs, argtypes, Values, Nulls, @@ -528,6 +551,45 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes, plan.cursor_options = cursorOptions; plan.nargs = nargs; plan.argtypes = argtypes; + plan.parserSetup = NULL; + plan.parserSetupArg = NULL; + + _SPI_prepare_plan(src, &plan, NULL); + + /* copy plan to procedure context */ + result = _SPI_copy_plan(&plan, _SPI_current->procCxt); + + _SPI_end_call(true); + + return result; +} + +SPIPlanPtr +SPI_prepare_params(const char *src, + ParserSetupHook parserSetup, + void *parserSetupArg, + int cursorOptions) +{ + _SPI_plan plan; + SPIPlanPtr result; + + if (src == NULL) + { + SPI_result = SPI_ERROR_ARGUMENT; + return NULL; + } + + SPI_result = _SPI_begin_call(true); + if (SPI_result < 0) + return NULL; + + memset(&plan, 0, sizeof(_SPI_plan)); + plan.magic = _SPI_PLAN_MAGIC; + plan.cursor_options = cursorOptions; + plan.nargs = 0; + plan.argtypes = NULL; + plan.parserSetup = parserSetup; + plan.parserSetupArg = parserSetupArg; _SPI_prepare_plan(src, &plan, NULL); @@ -954,8 +1016,21 @@ SPI_cursor_open(const char *name, SPIPlanPtr plan, Datum *Values, const char *Nulls, bool read_only) { - return SPI_cursor_open_internal(name, plan, Values, Nulls, - read_only, 0); + Portal portal; + ParamListInfo paramLI; + + /* build transient ParamListInfo in caller's context */ + paramLI = _SPI_convert_params(plan->nargs, plan->argtypes, + Values, Nulls, + 0); + + portal = SPI_cursor_open_internal(name, plan, paramLI, read_only); + + /* done with the transient ParamListInfo */ + if (paramLI) + pfree(paramLI); + + return portal; } @@ -992,7 +1067,10 @@ SPI_cursor_open_with_args(const char *name, plan.cursor_options = cursorOptions; plan.nargs = nargs; plan.argtypes = argtypes; + plan.parserSetup = NULL; + plan.parserSetupArg = NULL; + /* build transient ParamListInfo in executor context */ paramLI = _SPI_convert_params(nargs, argtypes, Values, Nulls, PARAM_FLAG_CONST); @@ -1007,8 +1085,7 @@ SPI_cursor_open_with_args(const char *name, /* SPI_cursor_open_internal must be called in procedure memory context */ _SPI_procmem(); - result = SPI_cursor_open_internal(name, &plan, Values, Nulls, - read_only, PARAM_FLAG_CONST); + result = SPI_cursor_open_internal(name, &plan, paramLI, read_only); /* And clean up */ _SPI_curid++; @@ -1019,24 +1096,35 @@ SPI_cursor_open_with_args(const char *name, /* + * SPI_cursor_open_with_paramlist() + * + * Same as SPI_cursor_open except that parameters (if any) are passed + * as a ParamListInfo, which supports dynamic parameter set determination + */ +Portal +SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan, + ParamListInfo params, bool read_only) +{ + return SPI_cursor_open_internal(name, plan, params, read_only); +} + + +/* * SPI_cursor_open_internal() * - * Common code for SPI_cursor_open and SPI_cursor_open_with_args + * Common code for SPI_cursor_open variants */ static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, - Datum *Values, const char *Nulls, - bool read_only, int pflags) + ParamListInfo paramLI, bool read_only) { CachedPlanSource *plansource; CachedPlan *cplan; List *stmt_list; char *query_string; - ParamListInfo paramLI; Snapshot snapshot; MemoryContext oldcontext; Portal portal; - int k; /* * Check that the plan is something the Portal code will special-case as @@ -1082,54 +1170,15 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, portal = CreatePortal(name, false, false); } - /* - * Prepare to copy stuff into the portal's memory context. We do all this - * copying first, because it could possibly fail (out-of-memory) and we - * don't want a failure to occur between RevalidateCachedPlan and - * PortalDefineQuery; that would result in leaking our plancache refcount. - */ - oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - /* Copy the plan's query string into the portal */ - query_string = pstrdup(plansource->query_string); - - /* If the plan has parameters, copy them into the portal */ - if (plan->nargs > 0) - { - /* sizeof(ParamListInfoData) includes the first array element */ - paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) + - (plan->nargs - 1) *sizeof(ParamExternData)); - paramLI->numParams = plan->nargs; - - for (k = 0; k < plan->nargs; k++) - { - ParamExternData *prm = ¶mLI->params[k]; - - prm->ptype = plan->argtypes[k]; - prm->pflags = pflags; - prm->isnull = (Nulls && Nulls[k] == 'n'); - if (prm->isnull) - { - /* nulls just copy */ - prm->value = Values[k]; - } - else - { - /* pass-by-ref values must be copied into portal context */ - int16 paramTypLen; - bool paramTypByVal; - - get_typlenbyval(prm->ptype, ¶mTypLen, ¶mTypByVal); - prm->value = datumCopy(Values[k], - paramTypByVal, paramTypLen); - } - } - } - else - paramLI = NULL; - - MemoryContextSwitchTo(oldcontext); + query_string = MemoryContextStrdup(PortalGetHeapMemory(portal), + plansource->query_string); + /* + * Note: we mustn't have any failure occur between RevalidateCachedPlan + * and PortalDefineQuery; that would result in leaking our plancache + * refcount. + */ if (plan->saved) { /* Replan if needed, and increment plan refcount for portal */ @@ -1221,6 +1270,19 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, } /* + * If the plan has parameters, copy them into the portal. Note that + * this must be done after revalidating the plan, because in dynamic + * parameter cases the set of parameters could have changed during + * re-parsing. + */ + if (paramLI) + { + oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + paramLI = copyParamList(paramLI); + MemoryContextSwitchTo(oldcontext); + } + + /* * Start portal execution. */ PortalStart(portal, paramLI, snapshot); @@ -1588,11 +1650,12 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self) /* * Parse and plan a querystring. * - * At entry, plan->argtypes, plan->nargs, and plan->cursor_options must be - * valid. 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 types embedded in the plan! + * 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 however that the result trees are all in CurrentMemoryContext @@ -1605,8 +1668,6 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams) List *plancache_list; ListCell *list_item; ErrorContextCallback spierrcontext; - Oid *argtypes = plan->argtypes; - int nargs = plan->nargs; int cursor_options = plan->cursor_options; /* @@ -1623,8 +1684,8 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams) raw_parsetree_list = pg_parse_query(src); /* - * Do parse analysis and rule rewrite for each raw parsetree, then cons up - * a phony plancache entry for each one. + * Do parse analysis, rule rewrite, and planning for each raw parsetree, + * then cons up a phony plancache entry for each one. */ plancache_list = NIL; @@ -1635,9 +1696,27 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams) CachedPlanSource *plansource; CachedPlan *cplan; - /* Need a copyObject here to keep parser from modifying raw tree */ - stmt_list = pg_analyze_and_rewrite(copyObject(parsetree), - src, argtypes, nargs); + /* + * Parameter datatypes are driven by parserSetup hook if provided, + * otherwise we use the fixed parameter list. + */ + if (plan->parserSetup != NULL) + { + Assert(plan->nargs == 0); + /* Need a copyObject here to keep parser from modifying raw tree */ + stmt_list = pg_analyze_and_rewrite_params(copyObject(parsetree), + src, + plan->parserSetup, + plan->parserSetupArg); + } + else + { + /* Need a copyObject here to keep parser from modifying raw tree */ + stmt_list = pg_analyze_and_rewrite(copyObject(parsetree), + src, + plan->argtypes, + plan->nargs); + } stmt_list = pg_plan_queries(stmt_list, cursor_options, boundParams); plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource)); @@ -1647,8 +1726,10 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams) /* cast-away-const here is a bit ugly, but there's no reason to copy */ plansource->query_string = (char *) src; plansource->commandTag = CreateCommandTag(parsetree); - plansource->param_types = argtypes; - plansource->num_params = nargs; + plansource->param_types = plan->argtypes; + plansource->num_params = plan->nargs; + plansource->parserSetup = plan->parserSetup; + plansource->parserSetupArg = plan->parserSetupArg; plansource->fully_planned = true; plansource->fixed_result = false; /* no need to set search_path, generation or saved_xmin */ @@ -1921,7 +2002,7 @@ fail: } /* - * Convert query parameters to form wanted by planner and executor + * Convert arrays of query parameters to form wanted by planner and executor */ static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes, @@ -1937,6 +2018,11 @@ _SPI_convert_params(int nargs, Oid *argtypes, /* sizeof(ParamListInfoData) includes the first array element */ paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) + (nargs - 1) *sizeof(ParamExternData)); + /* we have static list of params, so no hooks needed */ + paramLI->paramFetch = NULL; + paramLI->paramFetchArg = NULL; + paramLI->parserSetup = NULL; + paramLI->parserSetupArg = NULL; paramLI->numParams = nargs; for (i = 0; i < nargs; i++) @@ -2222,6 +2308,8 @@ _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt) } else newplan->argtypes = NULL; + newplan->parserSetup = plan->parserSetup; + newplan->parserSetupArg = plan->parserSetupArg; foreach(lc, plan->plancache_list) { @@ -2241,6 +2329,8 @@ _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt) newsource->commandTag = plansource->commandTag; newsource->param_types = newplan->argtypes; newsource->num_params = newplan->nargs; + newsource->parserSetup = newplan->parserSetup; + newsource->parserSetupArg = newplan->parserSetupArg; newsource->fully_planned = plansource->fully_planned; newsource->fixed_result = plansource->fixed_result; /* no need to worry about seach_path, generation or saved_xmin */ @@ -2298,6 +2388,8 @@ _SPI_save_plan(SPIPlanPtr plan) } else newplan->argtypes = NULL; + newplan->parserSetup = plan->parserSetup; + newplan->parserSetupArg = plan->parserSetupArg; foreach(lc, plan->plancache_list) { @@ -2317,6 +2409,10 @@ _SPI_save_plan(SPIPlanPtr plan) cplan->stmt_list, true, false); + if (newplan->parserSetup != NULL) + CachedPlanSetParserHook(newsource, + newplan->parserSetup, + newplan->parserSetupArg); newplan->plancache_list = lappend(newplan->plancache_list, newsource); } |