aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/spi.c')
-rw-r--r--src/backend/executor/spi.c242
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 = &paramLI->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, &paramTypLen, &paramTypByVal);
- 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);
}