aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/explain.c13
-rw-r--r--src/backend/commands/prepare.c7
-rw-r--r--src/backend/executor/execCurrent.c16
-rw-r--r--src/backend/executor/execQual.c16
-rw-r--r--src/backend/executor/functions.c7
-rw-r--r--src/backend/executor/spi.c242
-rw-r--r--src/backend/nodes/params.c82
-rw-r--r--src/backend/tcop/postgres.c53
-rw-r--r--src/backend/utils/cache/plancache.c53
9 files changed, 364 insertions, 125 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 1260ca00c23..21fa3add4f6 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.192 2009/10/12 18:10:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.193 2009/11/04 22:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -107,8 +107,6 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
ParamListInfo params, DestReceiver *dest)
{
ExplainState es;
- Oid *param_types;
- int num_params;
TupOutputState *tstate;
List *rewritten;
ListCell *lc;
@@ -150,9 +148,6 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
opt->defname)));
}
- /* Convert parameter type data to the form parser wants */
- getParamListTypes(params, &param_types, &num_params);
-
/*
* Run parse analysis and rewrite. Note this also acquires sufficient
* locks on the source table(s).
@@ -163,8 +158,10 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
* executed repeatedly. (See also the same hack in DECLARE CURSOR and
* PREPARE.) XXX FIXME someday.
*/
- rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
- queryString, param_types, num_params);
+ rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query),
+ queryString,
+ (ParserSetupHook) setupParserWithParamList,
+ params);
/* emit opening boilerplate */
ExplainBeginOutput(&es);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 56a16401f35..021c2daf26d 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -10,7 +10,7 @@
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.99 2009/08/10 05:46:50 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.100 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -379,6 +379,11 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
paramLI = (ParamListInfo)
palloc(sizeof(ParamListInfoData) +
(num_params - 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 = num_params;
i = 0;
diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c
index a4103332c40..35dc05a52b8 100644
--- a/src/backend/executor/execCurrent.c
+++ b/src/backend/executor/execCurrent.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.12 2009/10/26 02:26:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.13 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -217,9 +217,21 @@ fetch_param_value(ExprContext *econtext, int paramId)
{
ParamExternData *prm = &paramInfo->params[paramId - 1];
+ /* give hook a chance in case parameter is dynamic */
+ if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
+ (*paramInfo->paramFetch) (paramInfo, paramId);
+
if (OidIsValid(prm->ptype) && !prm->isnull)
{
- Assert(prm->ptype == REFCURSOROID);
+ /* safety check in case hook did something unexpected */
+ if (prm->ptype != REFCURSOROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
+ paramId,
+ format_type_be(prm->ptype),
+ format_type_be(REFCURSOROID))));
+
/* We know that refcursor uses text's I/O routines */
return TextDatumGetCString(prm->value);
}
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index fdfbd999f4f..226e15546fd 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.253 2009/10/26 02:26:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.254 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -882,9 +882,21 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
{
ParamExternData *prm = &paramInfo->params[thisParamId - 1];
+ /* give hook a chance in case parameter is dynamic */
+ if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
+ (*paramInfo->paramFetch) (paramInfo, thisParamId);
+
if (OidIsValid(prm->ptype))
{
- Assert(prm->ptype == expression->paramtype);
+ /* safety check in case hook did something unexpected */
+ if (prm->ptype != expression->paramtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
+ thisParamId,
+ format_type_be(prm->ptype),
+ format_type_be(expression->paramtype))));
+
*isNull = prm->isnull;
return prm->value;
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index fe25798a21e..2934e51161e 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.135 2009/06/11 17:25:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.136 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -526,6 +526,11 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
/* 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;
fcache->paramLI = paramLI;
}
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);
}
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index e7eeb2df015..111276c348c 100644
--- a/src/backend/nodes/params.c
+++ b/src/backend/nodes/params.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.11 2009/01/01 17:23:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.12 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "nodes/params.h"
+#include "parser/parse_param.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
@@ -24,6 +25,11 @@
* Copy a ParamListInfo structure.
*
* The result is allocated in CurrentMemoryContext.
+ *
+ * Note: the intent of this function is to make a static, self-contained
+ * set of parameter values. If dynamic parameter hooks are present, we
+ * intentionally do not copy them into the result. Rather, we forcibly
+ * instantiate all available parameter values and copy the datum values.
*/
ParamListInfo
copyParamList(ParamListInfo from)
@@ -40,54 +46,76 @@ copyParamList(ParamListInfo from)
(from->numParams - 1) *sizeof(ParamExternData);
retval = (ParamListInfo) palloc(size);
- memcpy(retval, from, size);
+ retval->paramFetch = NULL;
+ retval->paramFetchArg = NULL;
+ retval->parserSetup = NULL;
+ retval->parserSetupArg = NULL;
+ retval->numParams = from->numParams;
- /*
- * Flat-copy is not good enough for pass-by-ref data values, so make a
- * pass over the array to copy those.
- */
- for (i = 0; i < retval->numParams; i++)
+ for (i = 0; i < from->numParams; i++)
{
- ParamExternData *prm = &retval->params[i];
+ ParamExternData *oprm = &from->params[i];
+ ParamExternData *nprm = &retval->params[i];
int16 typLen;
bool typByVal;
- if (prm->isnull || !OidIsValid(prm->ptype))
+ /* give hook a chance in case parameter is dynamic */
+ if (!OidIsValid(oprm->ptype) && from->paramFetch != NULL)
+ (*from->paramFetch) (from, i+1);
+
+ /* flat-copy the parameter info */
+ *nprm = *oprm;
+
+ /* need datumCopy in case it's a pass-by-reference datatype */
+ if (nprm->isnull || !OidIsValid(nprm->ptype))
continue;
- get_typlenbyval(prm->ptype, &typLen, &typByVal);
- prm->value = datumCopy(prm->value, typByVal, typLen);
+ get_typlenbyval(nprm->ptype, &typLen, &typByVal);
+ nprm->value = datumCopy(nprm->value, typByVal, typLen);
}
return retval;
}
/*
- * Extract an array of parameter type OIDs from a ParamListInfo.
+ * Set up the parser to treat the given list of run-time parameters
+ * as available external parameters during parsing of a new query.
*
- * The result is allocated in CurrentMemoryContext.
+ * Note that the parser doesn't actually care about the *values* of the given
+ * parameters, only about their *types*. Also, the code that originally
+ * provided the ParamListInfo may have provided a setupHook, which should
+ * override applying parse_fixed_parameters().
*/
void
-getParamListTypes(ParamListInfo params,
- Oid **param_types, int *num_params)
+setupParserWithParamList(struct ParseState *pstate,
+ ParamListInfo params)
{
- Oid *ptypes;
- int i;
+ if (params == NULL) /* no params, nothing to do */
+ return;
- if (params == NULL || params->numParams <= 0)
+ /* If there is a parserSetup hook, it gets to do this */
+ if (params->parserSetup != NULL)
{
- *param_types = NULL;
- *num_params = 0;
+ (*params->parserSetup) (pstate, params->parserSetupArg);
return;
}
- ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
- *param_types = ptypes;
- *num_params = params->numParams;
-
- for (i = 0; i < params->numParams; i++)
+ /* Else, treat any available parameters as being of fixed type */
+ if (params->numParams > 0)
{
- ParamExternData *prm = &params->params[i];
+ Oid *ptypes;
+ int i;
+
+ ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
+ for (i = 0; i < params->numParams; i++)
+ {
+ ParamExternData *prm = &params->params[i];
+
+ /* give hook a chance in case parameter is dynamic */
+ if (!OidIsValid(prm->ptype) && params->paramFetch != NULL)
+ (*params->paramFetch) (params, i+1);
- ptypes[i] = prm->ptype;
+ ptypes[i] = prm->ptype;
+ }
+ parse_fixed_parameters(pstate, ptypes, params->numParams);
}
}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 67deea79624..b6a892a30f5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.574 2009/10/08 22:34:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.575 2009/11/04 22:26:06 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -628,6 +628,52 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
}
/*
+ * Do parse analysis and rewriting. This is the same as pg_analyze_and_rewrite
+ * except that external-parameter resolution is determined by parser callback
+ * hooks instead of a fixed list of parameter datatypes.
+ */
+List *
+pg_analyze_and_rewrite_params(Node *parsetree,
+ const char *query_string,
+ ParserSetupHook parserSetup,
+ void *parserSetupArg)
+{
+ ParseState *pstate;
+ Query *query;
+ List *querytree_list;
+
+ Assert(query_string != NULL); /* required as of 8.4 */
+
+ TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string);
+
+ /*
+ * (1) Perform parse analysis.
+ */
+ if (log_parser_stats)
+ ResetUsage();
+
+ pstate = make_parsestate(NULL);
+ pstate->p_sourcetext = query_string;
+ (*parserSetup) (pstate, parserSetupArg);
+
+ query = transformStmt(pstate, parsetree);
+
+ free_parsestate(pstate);
+
+ if (log_parser_stats)
+ ShowUsage("PARSE ANALYSIS STATISTICS");
+
+ /*
+ * (2) Rewrite the queries, as necessary
+ */
+ querytree_list = pg_rewrite_query(query);
+
+ TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string);
+
+ return querytree_list;
+}
+
+/*
* Perform rewriting of a query produced by parse analysis.
*
* Note: query must just have come from the parser, because we do not do
@@ -1536,6 +1582,11 @@ exec_bind_message(StringInfo input_message)
/* sizeof(ParamListInfoData) includes the first array element */
params = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(numParams - 1) *sizeof(ParamExternData));
+ /* we have static list of params, so no hooks needed */
+ params->paramFetch = NULL;
+ params->paramFetchArg = NULL;
+ params->parserSetup = NULL;
+ params->parserSetupArg = NULL;
params->numParams = numParams;
for (paramno = 0; paramno < numParams; paramno++)
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index eb196ebb56a..b473b7a1598 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -35,7 +35,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.30 2009/10/26 02:26:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.31 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -99,8 +99,8 @@ InitPlanCache(void)
* raw_parse_tree: output of raw_parser()
* query_string: original query text (as of PG 8.4, must not be NULL)
* commandTag: compile-time-constant tag for query, or NULL if empty query
- * param_types: array of parameter type OIDs, or NULL if none
- * num_params: number of parameters
+ * param_types: array of fixed parameter type OIDs, or NULL if none
+ * num_params: number of fixed parameters
* cursor_options: options bitmask that was/will be passed to planner
* stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
* fully_planned: are we caching planner or rewriter output?
@@ -156,6 +156,9 @@ CreateCachedPlan(Node *raw_parse_tree,
else
plansource->param_types = NULL;
plansource->num_params = num_params;
+ /* these can be set later with CachedPlanSetParserHook: */
+ plansource->parserSetup = NULL;
+ plansource->parserSetupArg = NULL;
plansource->cursor_options = cursor_options;
plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result;
@@ -240,6 +243,9 @@ FastCreateCachedPlan(Node *raw_parse_tree,
plansource->commandTag = commandTag; /* no copying needed */
plansource->param_types = param_types;
plansource->num_params = num_params;
+ /* these can be set later with CachedPlanSetParserHook: */
+ plansource->parserSetup = NULL;
+ plansource->parserSetupArg = NULL;
plansource->cursor_options = cursor_options;
plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result;
@@ -275,6 +281,27 @@ FastCreateCachedPlan(Node *raw_parse_tree,
}
/*
+ * CachedPlanSetParserHook: set up to use parser callback hooks
+ *
+ * Use this when a caller wants to manage parameter information via parser
+ * callbacks rather than a fixed parameter-types list. Beware that the
+ * information pointed to by parserSetupArg must be valid for as long as
+ * the cached plan might be replanned!
+ */
+void
+CachedPlanSetParserHook(CachedPlanSource *plansource,
+ ParserSetupHook parserSetup,
+ void *parserSetupArg)
+{
+ /* Must not have specified a fixed parameter-types list */
+ Assert(plansource->param_types == NULL);
+ Assert(plansource->num_params == 0);
+ /* OK, save hook info */
+ plansource->parserSetup = parserSetup;
+ plansource->parserSetupArg = parserSetupArg;
+}
+
+/*
* StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
*
* Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
@@ -466,6 +493,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
if (!plan)
{
bool snapshot_set = false;
+ Node *rawtree;
List *slist;
TupleDesc resultDesc;
@@ -491,14 +519,19 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
/*
* Run parse analysis and rule rewriting. The parser tends to
* scribble on its input, so we must copy the raw parse tree to
- * prevent corruption of the cache. Note that we do not use
- * parse_analyze_varparams(), assuming that the caller never wants the
- * parameter types to change from the original values.
+ * prevent corruption of the cache.
*/
- slist = pg_analyze_and_rewrite(copyObject(plansource->raw_parse_tree),
- plansource->query_string,
- plansource->param_types,
- plansource->num_params);
+ rawtree = copyObject(plansource->raw_parse_tree);
+ if (plansource->parserSetup != NULL)
+ slist = pg_analyze_and_rewrite_params(rawtree,
+ plansource->query_string,
+ plansource->parserSetup,
+ plansource->parserSetupArg);
+ else
+ slist = pg_analyze_and_rewrite(rawtree,
+ plansource->query_string,
+ plansource->param_types,
+ plansource->num_params);
if (plansource->fully_planned)
{