diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/explain.c | 13 | ||||
-rw-r--r-- | src/backend/commands/prepare.c | 7 | ||||
-rw-r--r-- | src/backend/executor/execCurrent.c | 16 | ||||
-rw-r--r-- | src/backend/executor/execQual.c | 16 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 7 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 242 | ||||
-rw-r--r-- | src/backend/nodes/params.c | 82 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 53 | ||||
-rw-r--r-- | src/backend/utils/cache/plancache.c | 53 |
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, ¶m_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 = ¶mInfo->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 = ¶mInfo->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 = ¶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); } 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 = ¶ms->params[i]; + Oid *ptypes; + int i; + + ptypes = (Oid *) palloc(params->numParams * sizeof(Oid)); + for (i = 0; i < params->numParams; i++) + { + ParamExternData *prm = ¶ms->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) { |