-<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.65 2006/06/18 15:38:36 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.66 2006/09/06 20:40:47 tgl Exp $ -->
<chapter id="protocol">
<title>Frontend/Backend Protocol</title>
into multiple steps. The state retained between steps is represented
by two types of objects: <firstterm>prepared statements</> and
<firstterm>portals</>. A prepared statement represents the result of
- parsing, semantic analysis, and planning of a textual query string. A
- prepared statement is not necessarily ready to execute, because it may
+ parsing, semantic analysis, and (optionally) planning of a textual query
+ string.
+ A prepared statement is not necessarily ready to execute, because it may
lack specific values for <firstterm>parameters</>. A portal represents
a ready-to-execute or already-partially-executed statement, with any
missing parameter values filled in. (For <command>SELECT</> statements,
<para>
Query planning for named prepared-statement objects occurs when the Parse
- message is received. If a query will be repeatedly executed with
+ message is processed. If a query will be repeatedly executed with
different parameters, it may be beneficial to send a single Parse message
containing a parameterized query, followed by multiple Bind
and Execute messages. This will avoid replanning the query on each
<para>
The unnamed prepared statement is likewise planned during Parse processing
if the Parse message defines no parameters. But if there are parameters,
- query planning is delayed until the first Bind message for the statement
- is received. The planner will consider the actual values of the parameters
- provided in the Bind message when planning the query.
+ query planning occurs during Bind processing instead. This allows the
+ planner to make use of the actual values of the parameters provided in
+ the Bind message when planning the query.
</para>
<note>
planning a parameterized query assigned to a named prepared-statement
object. This possible penalty is avoided when using the unnamed
statement, since it is not planned until actual parameter values are
- available.
- </para>
-
- <para>
- If a second or subsequent Bind referencing the unnamed prepared-statement
- object is received without an intervening Parse, the query is
- not replanned. The parameter values used in the first Bind message may
- produce a query plan that is only efficient for a subset of possible
- parameter values. To force replanning of the query for a fresh set of
- parameters, send another Parse message to replace the unnamed
- prepared-statement object.
+ available. The cost is that planning must occur afresh for each Bind,
+ even if the query stays the same.
</para>
</note>
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.150 2006/08/02 01:59:45 joe Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.151 2006/09/06 20:40:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/* plan the query */
- plan = planner(query, isCursor, cursorOptions, NULL);
+ plan = planner(query, isCursor, cursorOptions, params);
/*
* Update snapshot command ID to ensure this query sees results of any
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.53 2006/09/03 03:19:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.54 2006/09/06 20:40:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
errdetail("Cursors must be READ ONLY.")));
- plan = planner(query, true, stmt->options, NULL);
+ plan = planner(query, true, stmt->options, params);
/*
* Create a portal and copy the query and plan into its memory context.
* Copyright (c) 2002-2006, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.62 2006/08/29 02:11:29 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.63 2006/09/06 20:40:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
ParamExternData *prm = ¶mLI->params[i];
prm->ptype = lfirst_oid(la);
+ prm->pflags = 0;
prm->value = ExecEvalExprSwitchContext(n,
GetPerTupleExprContext(estate),
&prm->isnull,
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.105 2006/08/12 20:05:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.106 2006/09/06 20:40:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
prm->value = fcinfo->arg[i];
prm->isnull = fcinfo->argnull[i];
+ prm->pflags = 0;
prm->ptype = fcache->argtypes[i];
}
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.161 2006/09/03 03:19:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.162 2006/09/06 20:40:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
ParamExternData *prm = ¶mLI->params[k];
prm->ptype = spiplan->argtypes[k];
+ prm->pflags = 0;
prm->isnull = (Nulls && Nulls[k] == 'n');
if (prm->isnull)
{
prm->value = Values[k];
prm->isnull = (Nulls && Nulls[k] == 'n');
+ prm->pflags = 0;
prm->ptype = plan->argtypes[k];
}
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.219 2006/08/12 20:05:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.220 2006/09/06 20:40:47 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
*
* Currently the extra steps that are taken in this mode are:
* 1. Substitute values for Params, where a bound Param value has been made
- * available by the caller of planner().
+ * available by the caller of planner(), even if the Param isn't marked
+ * constant. This effectively means that we plan using the first supplied
+ * value of the Param.
* 2. Fold stable, as well as immutable, functions to constants.
*--------------------
*/
{
Param *param = (Param *) node;
- /* OK to try to substitute value? */
- if (context->estimate && param->paramkind == PARAM_EXTERN &&
- PlannerBoundParamList != NULL)
+ /* Look to see if we've been given a value for this Param */
+ if (param->paramkind == PARAM_EXTERN &&
+ PlannerBoundParamList != NULL &&
+ param->paramid > 0 &&
+ param->paramid <= PlannerBoundParamList->numParams)
{
- /* Look to see if we've been given a value for this Param */
- if (param->paramid > 0 &&
- param->paramid <= PlannerBoundParamList->numParams)
- {
- ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
+ ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
- if (OidIsValid(prm->ptype))
+ if (OidIsValid(prm->ptype))
+ {
+ /* OK to substitute parameter value? */
+ if (context->estimate || (prm->pflags & PARAM_FLAG_CONST))
{
/*
- * Found it, so return a Const representing the param
- * value. Note that we don't copy pass-by-ref datatypes,
- * so the Const will only be valid as long as the bound
- * parameter list exists. This is okay for intended uses
- * of estimate_expression_value().
+ * Return a Const representing the param value. Must copy
+ * pass-by-ref datatypes, since the Param might be in a
+ * memory context shorter-lived than our output plan
+ * should be.
*/
int16 typLen;
bool typByVal;
+ Datum pval;
Assert(prm->ptype == param->paramtype);
get_typlenbyval(param->paramtype, &typLen, &typByVal);
+ if (prm->isnull || typByVal)
+ pval = prm->value;
+ else
+ pval = datumCopy(prm->value, typByVal, typLen);
return (Node *) makeConst(param->paramtype,
(int) typLen,
- prm->value,
+ pval,
prm->isnull,
typByVal);
}
* stage. In particular, it handles List nodes since a cnf-ified qual clause
* will have List structure at the top level, and it handles TargetEntry nodes
* so that a scan of a target list can be handled without additional code.
- * (But only the "expr" part of a TargetEntry is examined, unless the walker
- * chooses to process TargetEntry nodes specially.) Also, RangeTblRef,
- * FromExpr, JoinExpr, and SetOperationStmt nodes are handled, so that query
- * jointrees and setOperation trees can be processed without additional code.
+ * Also, RangeTblRef, FromExpr, JoinExpr, and SetOperationStmt nodes are
+ * handled, so that query jointrees and setOperation trees can be processed
+ * without additional code.
*
* expression_tree_walker will handle SubLink nodes by recursing normally
* into the "testexpr" subtree (which is an expression belonging to the outer
return true;
if (range_table_walker(query->rtable, walker, context, flags))
return true;
+ if (query->utilityStmt)
+ {
+ /*
+ * Certain utility commands contain general-purpose Querys embedded
+ * in them --- if this is one, invoke the walker on the sub-Query.
+ */
+ if (IsA(query->utilityStmt, CopyStmt))
+ {
+ if (walker(((CopyStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ if (IsA(query->utilityStmt, DeclareCursorStmt))
+ {
+ if (walker(((DeclareCursorStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ if (IsA(query->utilityStmt, ExplainStmt))
+ {
+ if (walker(((ExplainStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ if (IsA(query->utilityStmt, PrepareStmt))
+ {
+ if (walker(((PrepareStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ if (IsA(query->utilityStmt, ViewStmt))
+ {
+ if (walker(((ViewStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ }
return false;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.505 2006/09/03 03:19:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.506 2006/09/06 20:40:48 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
* statement, we assume the statement isn't going to hang around long, so
* getting rid of temp space quickly is probably not worth the costs of
* copying parse/plan trees. So in this case, we set up a special context
- * for the unnamed statement, and do all the parsing/planning therein.
+ * for the unnamed statement, and do all the parsing work therein.
*/
is_named = (stmt_name[0] != '\0');
if (is_named)
PreparedStatement *pstmt;
Portal portal;
ParamListInfo params;
+ List *plan_list;
StringInfoData bind_values_str;
pgstat_report_activity("<BIND>");
{
Oid ptype = lfirst_oid(l);
int32 plength;
+ Datum pval;
bool isNull;
StringInfoData pbuf;
char csave;
else
pstring = pg_client_to_server(pbuf.data, plength);
- params->params[paramno].value =
- OidInputFunctionCall(typinput,
- pstring,
- typioparam,
- -1);
+ pval = OidInputFunctionCall(typinput, pstring, typioparam, -1);
/* Save the parameter values */
appendStringInfo(&bind_values_str, "%s$%d = ",
else
bufptr = &pbuf;
- params->params[paramno].value = OidReceiveFunctionCall(typreceive,
- bufptr,
- typioparam,
- -1);
+ pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1);
/* Trouble if it didn't eat the whole buffer */
if (!isNull && pbuf.cursor != pbuf.len)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unsupported format code: %d",
pformat)));
+ pval = 0; /* keep compiler quiet */
}
/* Restore message buffer contents */
if (!isNull)
pbuf.data[plength] = csave;
+ params->params[paramno].value = pval;
params->params[paramno].isnull = isNull;
+ /*
+ * We mark the params as CONST. This has no effect if we already
+ * did planning, but if we didn't, it licenses the planner to
+ * substitute the parameters directly into the one-shot plan we
+ * will generate below.
+ */
+ params->params[paramno].pflags = PARAM_FLAG_CONST;
params->params[paramno].ptype = ptype;
paramno++;
/*
* If we didn't plan the query before, do it now. This allows the planner
- * to make use of the concrete parameter values we now have.
+ * to make use of the concrete parameter values we now have. Because we
+ * use PARAM_FLAG_CONST, the plan is good only for this set of param
+ * values, and so we generate the plan in the portal's own memory context
+ * where it will be thrown away after use. As in exec_parse_message,
+ * we make no attempt to recover planner temporary memory until the end
+ * of the operation.
*
- * This happens only for unnamed statements, and so switching into the
- * statement context for planning is correct (see notes in
- * exec_parse_message).
+ * XXX because the planner has a bad habit of scribbling on its input,
+ * we have to make a copy of the parse trees, just in case someone binds
+ * and executes an unnamed statement multiple times. FIXME someday
*/
if (pstmt->plan_list == NIL && pstmt->query_list != NIL)
{
- MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context);
+ MemoryContext oldContext;
- pstmt->plan_list = pg_plan_queries(pstmt->query_list, params, true);
+ oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+ plan_list = pg_plan_queries(copyObject(pstmt->query_list),
+ params,
+ true);
MemoryContextSwitchTo(oldContext);
}
+ else
+ plan_list = pstmt->plan_list;
/*
* Define portal and start execution.
bind_values_str.len ? pstrdup(bind_values_str.data) : NULL,
pstmt->commandTag,
pstmt->query_list,
- pstmt->plan_list,
+ plan_list,
pstmt->context);
PortalStart(portal, params, InvalidSnapshot);
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.31 2006/04/22 01:26:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.32 2006/09/06 20:40:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Although parameter numbers are normally consecutive, we allow
* ptype == InvalidOid to signal an unused array entry.
*
+ * PARAM_FLAG_CONST signals the planner that it may treat this parameter
+ * as a constant (i.e., generate a plan that works only for this value
+ * of the parameter).
+ *
* Although the data structure is really an array, not a list, we keep
* the old typedef name to avoid unnecessary code changes.
* ----------------
*/
+#define PARAM_FLAG_CONST 0x0001 /* parameter is constant */
+
typedef struct ParamExternData
{
Datum value; /* parameter value */
bool isnull; /* is it NULL? */
+ uint16 pflags; /* flag bits, see above */
Oid ptype; /* parameter's datatype, or 0 */
} ParamExternData;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.177 2006/08/27 23:47:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.178 2006/09/06 20:40:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
ParamExternData *prm = ¶mLI->params[i];
PLpgSQL_datum *datum = estate->datums[expr->params[i]];
+ prm->pflags = 0;
exec_eval_datum(estate, datum, expr->plan_argtypes[i],
&prm->ptype,
&prm->value, &prm->isnull);