Commit 7643bed5 authored by Tom Lane's avatar Tom Lane

When using extended-query protocol, postpone planning of unnamed statements

until Bind is received, so that actual parameter values are visible to the
planner.  Make use of the parameter values for estimation purposes (but
don't fold them into the actual plan).  This buys back most of the
potential loss of plan quality that ensues from using out-of-line
parameters instead of putting literal values right into the query text.

This patch creates a notion of constant-folding expressions 'for
estimation purposes only', in which case we can be more aggressive than
the normal eval_const_expressions() logic can be.  Right now the only
difference in behavior is inserting bound values for Params, but it will
be interesting to look at other possibilities.  One that we've seen
come up repeatedly is reducing now() and related functions to current
values, so that queries like ... WHERE timestampcol > now() - '1 day'
have some chance of being planned effectively.

Oliver Jowett, with some kibitzing from Tom Lane.
parent 5fe8c7d6
<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.51 2004/03/21 22:29:10 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.52 2004/06/11 01:08:33 tgl Exp $ -->
<chapter id="protocol">
<title>Frontend/Backend Protocol</title>
......@@ -684,6 +684,46 @@
</para>
</note>
<para>
Query planning for named prepared-statement objects occurs when the Parse
message is received. 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
execution.
</para>
<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.
</para>
<note>
<para>
Query plans generated from a parameterized query may be less
efficient than query plans generated from an equivalent query with actual
parameter values substituted. The query planner cannot make decisions
based on actual parameter values (for example, index selectivity) when
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.
</para>
</note>
<para>
If successfully created, a named portal object lasts till the end of the
current transaction, unless explicitly destroyed. An unnamed portal is
......
......@@ -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.121 2004/05/26 04:41:10 neilc Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.122 2004/06/11 01:08:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -176,7 +176,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
}
/* plan the query */
plan = planner(query, isCursor, cursorOptions);
plan = planner(query, isCursor, cursorOptions, NULL);
/* Create a QueryDesc requesting no output */
queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
......
......@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.27 2004/05/26 04:41:11 neilc Exp $
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.28 2004/06/11 01:08:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -84,7 +84,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt)
errmsg("DECLARE CURSOR ... FOR UPDATE is not supported"),
errdetail("Cursors must be READ ONLY.")));
plan = planner(query, true, stmt->options);
plan = planner(query, true, stmt->options, NULL);
/*
* Create a portal and copy the query and plan into its memory
......
......@@ -10,7 +10,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.27 2004/05/26 04:41:11 neilc Exp $
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.28 2004/06/11 01:08:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -91,7 +91,7 @@ PrepareQuery(PrepareStmt *stmt)
query_list = QueryRewrite(stmt->query);
/* Generate plans for queries. Snapshot is already set. */
plan_list = pg_plan_queries(query_list, false);
plan_list = pg_plan_queries(query_list, NULL, false);
/* Save the results. */
StorePreparedStatement(stmt->name,
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.81 2004/05/26 04:41:15 neilc Exp $
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.82 2004/06/11 01:08:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -100,7 +100,7 @@ init_execution_state(List *queryTree_list)
Plan *planTree;
execution_state *newes;
planTree = pg_plan_query(queryTree);
planTree = pg_plan_query(queryTree, NULL);
newes = (execution_state *) palloc(sizeof(execution_state));
if (preves)
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.117 2004/06/06 00:41:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.118 2004/06/11 01:08:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1125,7 +1125,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
QueryDesc *qdesc;
DestReceiver *dest;
planTree = pg_plan_query(queryTree);
planTree = pg_plan_query(queryTree, NULL);
plan_list = lappend(plan_list, planTree);
dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.67 2004/05/30 23:40:28 neilc Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.68 2004/06/11 01:08:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -487,16 +487,27 @@ clause_selectivity(Query *root,
}
}
}
else if (IsA(clause, Param))
{
/* XXX any way to do better? */
s1 = 1.0;
}
else if (IsA(clause, Const))
{
/* bool constant is pretty easy... */
s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0;
}
else if (IsA(clause, Param))
{
/* see if we can replace the Param */
Node *subst = estimate_expression_value(clause);
if (IsA(subst, Const))
{
/* bool constant is pretty easy... */
s1 = ((bool) ((Const *) subst)->constvalue) ? 1.0 : 0.0;
}
else
{
/* XXX any way to do better? */
s1 = (Selectivity) 0.5;
}
}
else if (not_clause(clause))
{
/* inverse of the selectivity of the underlying clause */
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.171 2004/05/30 23:40:29 neilc Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.172 2004/06/11 01:08:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -43,6 +43,9 @@
#include "utils/syscache.h"
ParamListInfo PlannerBoundParamList = NULL; /* current boundParams */
/* Expression kind codes for preprocess_expression */
#define EXPRKIND_QUAL 0
#define EXPRKIND_TARGET 1
......@@ -71,20 +74,24 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
*
*****************************************************************************/
Plan *
planner(Query *parse, bool isCursor, int cursorOptions)
planner(Query *parse, bool isCursor, int cursorOptions,
ParamListInfo boundParams)
{
double tuple_fraction;
Plan *result_plan;
Index save_PlannerQueryLevel;
List *save_PlannerParamList;
ParamListInfo save_PlannerBoundParamList;
/*
* The planner can be called recursively (an example is when
* eval_const_expressions tries to pre-evaluate an SQL function). So,
* these global state variables must be saved and restored.
*
* These vars cannot be moved into the Query structure since their whole
* purpose is communication across multiple sub-Queries.
* Query level and the param list cannot be moved into the Query structure
* since their whole purpose is communication across multiple sub-Queries.
* Also, boundParams is explicitly info from outside the Query, and so
* is likewise better handled as a global variable.
*
* Note we do NOT save and restore PlannerPlanId: it exists to assign
* unique IDs to SubPlan nodes, and we want those IDs to be unique for
......@@ -93,10 +100,12 @@ planner(Query *parse, bool isCursor, int cursorOptions)
*/
save_PlannerQueryLevel = PlannerQueryLevel;
save_PlannerParamList = PlannerParamList;
save_PlannerBoundParamList = PlannerBoundParamList;
/* Initialize state for handling outer-level references and params */
PlannerQueryLevel = 0; /* will be 1 in top-level subquery_planner */
PlannerParamList = NIL;
PlannerBoundParamList = boundParams;
/* Determine what fraction of the plan is likely to be scanned */
if (isCursor)
......@@ -139,6 +148,7 @@ planner(Query *parse, bool isCursor, int cursorOptions)
/* restore state for outer planner, if any */
PlannerQueryLevel = save_PlannerQueryLevel;
PlannerParamList = save_PlannerParamList;
PlannerBoundParamList = save_PlannerBoundParamList;
return result_plan;
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.175 2004/06/09 19:08:16 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.176 2004/06/11 01:08:54 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -28,6 +28,7 @@
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
......@@ -41,6 +42,12 @@
#include "utils/syscache.h"
typedef struct
{
List *active_fns;
bool estimate;
} eval_const_expressions_context;
typedef struct
{
int nargs;
......@@ -57,17 +64,20 @@ static bool contain_mutable_functions_walker(Node *node, void *context);
static bool contain_volatile_functions_walker(Node *node, void *context);
static bool contain_nonstrict_functions_walker(Node *node, void *context);
static bool set_coercionform_dontcare_walker(Node *node, void *context);
static Node *eval_const_expressions_mutator(Node *node, List *active_fns);
static Node *eval_const_expressions_mutator(Node *node,
eval_const_expressions_context *context);
static List *simplify_or_arguments(List *args,
bool *haveNull, bool *forceTrue);
static List *simplify_and_arguments(List *args,
bool *haveNull, bool *forceFalse);
static Expr *simplify_function(Oid funcid, Oid result_type, List *args,
bool allow_inline, List *active_fns);
bool allow_inline,
eval_const_expressions_context *context);
static Expr *evaluate_function(Oid funcid, Oid result_type, List *args,
HeapTuple func_tuple);
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
HeapTuple func_tuple, List *active_fns);
HeapTuple func_tuple,
eval_const_expressions_context *context);
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
int *usecounts);
static Node *substitute_actual_parameters_mutator(Node *node,
......@@ -1070,18 +1080,101 @@ set_coercionform_dontcare_walker(Node *node, void *context)
Node *
eval_const_expressions(Node *node)
{
/*
* The context for the mutator is a list of SQL functions being
* recursively simplified, so we start with an empty list.
eval_const_expressions_context context;
context.active_fns = NIL; /* nothing being recursively simplified */
context.estimate = false; /* safe transformations only */
return eval_const_expressions_mutator(node, &context);
}
/*
* estimate_expression_value
*
* This function attempts to estimate the value of an expression for
* planning purposes. It is in essence a more aggressive version of
* eval_const_expressions(): we will perform constant reductions that are
* not necessarily 100% safe, but are reasonable for estimation purposes.
*
* Currently the only such transform is to substitute values for Params,
* when a bound Param value has been made available by the caller of planner().
* In future we might consider other things, such as reducing now() to current
* time. (XXX seems like there could be a lot of scope for ideas here...
* but we might need more volatility classifications ...)
*/
return eval_const_expressions_mutator(node, NIL);
Node *
estimate_expression_value(Node *node)
{
eval_const_expressions_context context;
context.active_fns = NIL; /* nothing being recursively simplified */
context.estimate = true; /* unsafe transformations OK */
return eval_const_expressions_mutator(node, &context);
}
static Node *
eval_const_expressions_mutator(Node *node, List *active_fns)
eval_const_expressions_mutator(Node *node,
eval_const_expressions_context *context)
{
if (node == NULL)
return NULL;
if (IsA(node, Param))
{
Param *param = (Param *) node;
int thisParamKind = param->paramkind;
/* OK to try to substitute value? */
if (context->estimate && thisParamKind != PARAM_EXEC &&
PlannerBoundParamList != NULL)
{
ParamListInfo paramList = PlannerBoundParamList;
bool matchFound = false;
/* Search to see if we've been given a value for this Param */
while (paramList->kind != PARAM_INVALID && !matchFound)
{
if (thisParamKind == paramList->kind)
{
switch (thisParamKind)
{
case PARAM_NAMED:
if (strcmp(paramList->name, param->paramname) == 0)
matchFound = true;
break;
case PARAM_NUM:
if (paramList->id == param->paramid)
matchFound = true;
break;
default:
elog(ERROR, "unrecognized paramkind: %d",
thisParamKind);
}
}
if (!matchFound)
paramList++;
}
if (matchFound)
{
/*
* 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().
*/
int16 typLen;
bool typByVal;
get_typlenbyval(param->paramtype, &typLen, &typByVal);
return (Node *) makeConst(param->paramtype,
(int) typLen,
paramList->value,
paramList->isnull,
typByVal);
}
}
/* Not replaceable, so just copy the Param (no need to recurse) */
return (Node *) copyObject(param);
}
if (IsA(node, FuncExpr))
{
FuncExpr *expr = (FuncExpr *) node;
......@@ -1096,14 +1189,14 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
eval_const_expressions_mutator,
(void *) active_fns);
(void *) context);
/*
* Code for op/func reduction is pretty bulky, so split it out as
* a separate function.
*/
simple = simplify_function(expr->funcid, expr->funcresulttype, args,
true, active_fns);
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
......@@ -1134,7 +1227,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
eval_const_expressions_mutator,
(void *) active_fns);
(void *) context);
/*
* Need to get OID of underlying function. Okay to scribble on
......@@ -1147,7 +1240,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
* a separate function.
*/
simple = simplify_function(expr->opfuncid, expr->opresulttype, args,
true, active_fns);
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
......@@ -1182,7 +1275,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
eval_const_expressions_mutator,
(void *) active_fns);
(void *) context);
/*
* We must do our own check for NULLs because DistinctExpr has
......@@ -1226,7 +1319,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
* as a separate function.
*/
simple = simplify_function(expr->opfuncid, expr->opresulttype,
args, false, active_fns);
args, false, context);
if (simple) /* successfully simplified it */
{
/*
......@@ -1267,7 +1360,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
eval_const_expressions_mutator,
(void *) active_fns);
(void *) context);
switch (expr->boolop)
{
......@@ -1360,7 +1453,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
Node *arg;
arg = eval_const_expressions_mutator((Node *) relabel->arg,
active_fns);
context);
/*
* If we find stacked RelabelTypes (eg, from foo :: int :: oid) we
......@@ -1424,7 +1517,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
/* Simplify the test expression, if any */
newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
active_fns);
context);
/* Simplify the WHEN clauses */
newargs = NIL;
......@@ -1434,7 +1527,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
CaseWhen *casewhen = (CaseWhen *)
expression_tree_mutator((Node *) lfirst(arg),
eval_const_expressions_mutator,
(void *) active_fns);
(void *) context);
Assert(IsA(casewhen, CaseWhen));
if (casewhen->expr == NULL ||
......@@ -1464,7 +1557,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
/* Simplify the default result */
defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult,
active_fns);
context);
/*
* If no non-FALSE alternatives, CASE reduces to the default
......@@ -1494,7 +1587,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
Node *e;
e = eval_const_expressions_mutator((Node *) lfirst(element),
active_fns);
context);
if (!IsA(e, Const))
all_const = false;
newelems = lappend(newelems, e);
......@@ -1525,7 +1618,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
Node *e;
e = eval_const_expressions_mutator((Node *) lfirst(arg),
active_fns);
context);
/*
* We can remove null constants from the list. For a non-null
......@@ -1561,7 +1654,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
Node *arg;
arg = eval_const_expressions_mutator((Node *) fselect->arg,
active_fns);
context);
if (arg && IsA(arg, Var) &&
((Var *) arg)->varattno == InvalidAttrNumber)
{
......@@ -1595,7 +1688,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
* simplify constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
(void *) active_fns);
(void *) context);
}
/*
......@@ -1726,14 +1819,15 @@ simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse)
*
* Inputs are the function OID, actual result type OID (which is needed for
* polymorphic functions), and the pre-simplified argument list;
* also a list of already-active inline function expansions.
* also the context data for eval_const_expressions.
*
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function call.
*/
static Expr *
simplify_function(Oid funcid, Oid result_type, List *args,
bool allow_inline, List *active_fns)
bool allow_inline,
eval_const_expressions_context *context)
{
HeapTuple func_tuple;
Expr *newexpr;
......@@ -1756,7 +1850,7 @@ simplify_function(Oid funcid, Oid result_type, List *args,
if (!newexpr && allow_inline)
newexpr = inline_function(funcid, result_type, args,
func_tuple, active_fns);
func_tuple, context);
ReleaseSysCache(func_tuple);
......@@ -1860,7 +1954,8 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
*/
static Expr *
inline_function(Oid funcid, Oid result_type, List *args,
HeapTuple func_tuple, List *active_fns)
HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
bool polymorphic = false;
......@@ -1890,7 +1985,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
return NULL;
/* Check for recursive function, and give up trying to expand if so */
if (list_member_oid(active_fns, funcid))
if (list_member_oid(context->active_fns, funcid))
return NULL;
/* Check permission to call function (fail later, if not) */
......@@ -2083,8 +2178,9 @@ inline_function(Oid funcid, Oid result_type, List *args,
* Recursively try to simplify the modified expression. Here we must
* add the current function to the context list of active functions.
*/
newexpr = eval_const_expressions_mutator(newexpr,
lcons_oid(funcid, active_fns));
context->active_fns = lcons_oid(funcid, context->active_fns);
newexpr = eval_const_expressions_mutator(newexpr, context);
context->active_fns = list_delete_first(context->active_fns);
error_context_stack = sqlerrcontext.previous;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.419 2004/06/06 00:41:27 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.420 2004/06/11 01:09:00 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -630,7 +630,7 @@ pg_rewrite_queries(List *querytree_list)
/* Generate a plan for a single already-rewritten query. */
Plan *
pg_plan_query(Query *querytree)
pg_plan_query(Query *querytree, ParamListInfo boundParams)
{
Plan *plan;
......@@ -642,7 +642,7 @@ pg_plan_query(Query *querytree)
ResetUsage();
/* call the optimizer */
plan = planner(querytree, false, 0);
plan = planner(querytree, false, 0, boundParams);
if (log_planner_stats)
ShowUsage("PLANNER STATISTICS");
......@@ -687,7 +687,8 @@ pg_plan_query(Query *querytree)
* statements in the rewriter's output.)
*/
List *
pg_plan_queries(List *querytrees, bool needSnapshot)
pg_plan_queries(List *querytrees, ParamListInfo boundParams,
bool needSnapshot)
{
List *plan_list = NIL;
ListCell *query_list;
......@@ -709,7 +710,7 @@ pg_plan_queries(List *querytrees, bool needSnapshot)
SetQuerySnapshot();
needSnapshot = false;
}
plan = pg_plan_query(query);
plan = pg_plan_query(query, boundParams);
}
plan_list = lappend(plan_list, plan);
......@@ -867,7 +868,7 @@ exec_simple_query(const char *query_string)
querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0);
plantree_list = pg_plan_queries(querytree_list, true);
plantree_list = pg_plan_queries(querytree_list, NULL, true);
/* If we got a cancel signal in analysis or planning, quit */
CHECK_FOR_INTERRUPTS();
......@@ -1205,7 +1206,14 @@ exec_parse_message(const char *query_string, /* string to execute */
querytree_list = pg_rewrite_queries(querytree_list);
plantree_list = pg_plan_queries(querytree_list, true);
/*
* If this is the unnamed statement and it has parameters, defer
* query planning until Bind. Otherwise do it now.
*/
if (!is_named && numParams > 0)
plantree_list = NIL;
else
plantree_list = pg_plan_queries(querytree_list, NULL, true);
}
else
{
......@@ -1291,6 +1299,7 @@ exec_bind_message(StringInfo input_message)
PreparedStatement *pstmt;
Portal portal;
ParamListInfo params;
bool isaborted = IsAbortedTransactionBlockState();
pgstat_report_activity("<BIND>");
......@@ -1356,13 +1365,6 @@ exec_bind_message(StringInfo input_message)
else
portal = CreatePortal(portal_name, false, false);
PortalDefineQuery(portal,
pstmt->query_string,
pstmt->commandTag,
pstmt->query_list,
pstmt->plan_list,
pstmt->context);
/*
* Fetch parameters, if any, and store in the portal's memory context.
*
......@@ -1372,7 +1374,6 @@ exec_bind_message(StringInfo input_message)
*/
if (numParams > 0)
{
bool isaborted = IsAbortedTransactionBlockState();
ListCell *l;
MemoryContext oldContext;
......@@ -1516,8 +1517,32 @@ exec_bind_message(StringInfo input_message)
pq_getmsgend(input_message);
/*
* Start portal execution.
* 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.
*
* This happens only for unnamed statements, and so switching into
* the statement context for planning is correct (see notes in
* exec_parse_message).
*/
if (pstmt->plan_list == NIL && pstmt->query_list != NIL &&
!isaborted)
{
MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context);
pstmt->plan_list = pg_plan_queries(pstmt->query_list, params, true);
MemoryContextSwitchTo(oldContext);
}
/*
* Define portal and start execution.
*/
PortalDefineQuery(portal,
pstmt->query_string,
pstmt->commandTag,
pstmt->query_list,
pstmt->plan_list,
pstmt->context);
PortalStart(portal, params);
/*
......
......@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.160 2004/05/30 23:40:36 neilc Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.161 2004/06/11 01:09:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -2823,7 +2823,8 @@ convert_timevalue_to_scalar(Datum value, Oid typid)
*
* Outputs: (these are valid only if TRUE is returned)
* *vardata: gets information about variable (see examine_variable)
* *other: gets other clause argument, stripped of binary relabeling
* *other: gets other clause argument, stripped of binary relabeling,
* and aggressively reduced to a constant
* *varonleft: set TRUE if variable is on the left, FALSE if on the right
*
* Returns TRUE if a variable is identified, otherwise FALSE.
......@@ -2860,7 +2861,7 @@ get_restriction_variable(Query *root, List *args, int varRelid,
if (vardata->rel && rdata.rel == NULL)
{
*varonleft = true;
*other = rdata.var;
*other = estimate_expression_value(rdata.var);
/* Assume we need no ReleaseVariableStats(rdata) here */
return true;
}
......@@ -2868,7 +2869,7 @@ get_restriction_variable(Query *root, List *args, int varRelid,
if (vardata->rel == NULL && rdata.rel)
{
*varonleft = false;
*other = vardata->var;
*other = estimate_expression_value(vardata->var);
/* Assume we need no ReleaseVariableStats(*vardata) here */
*vardata = rdata;
return true;
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.73 2004/03/14 23:41:27 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.74 2004/06/11 01:09:12 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -67,6 +67,8 @@ extern void set_coercionform_dontcare(Node *node);
extern Node *eval_const_expressions(Node *node);
extern Node *estimate_expression_value(Node *node);
extern bool expression_tree_walker(Node *node, bool (*walker) (),
void *context);
extern Node *expression_tree_mutator(Node *node, Node *(*mutator) (),
......
......@@ -7,18 +7,22 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.28 2003/11/29 22:41:07 pgsql Exp $
* $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.29 2004/06/11 01:09:12 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PLANNER_H
#define PLANNER_H
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
extern Plan *planner(Query *parse, bool isCursor, int cursorOptions);
extern ParamListInfo PlannerBoundParamList; /* current boundParams */
extern Plan *planner(Query *parse, bool isCursor, int cursorOptions,
ParamListInfo boundParams);
extern Plan *subquery_planner(Query *parse, double tuple_fraction);
#endif /* PLANNER_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.66 2004/05/29 22:48:23 tgl Exp $
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.67 2004/06/11 01:09:22 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
......@@ -22,6 +22,7 @@
#include <setjmp.h>
#include "executor/execdesc.h"
#include "nodes/params.h"
#include "tcop/dest.h"
#include "utils/guc.h"
......@@ -55,8 +56,9 @@ extern List *pg_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(Node *parsetree,
Oid *paramTypes, int numParams);
extern List *pg_rewrite_queries(List *querytree_list);
extern Plan *pg_plan_query(Query *querytree);
extern List *pg_plan_queries(List *querytrees, bool needSnapshot);
extern Plan *pg_plan_query(Query *querytree, ParamListInfo boundParams);
extern List *pg_plan_queries(List *querytrees, ParamListInfo boundParams,
bool needSnapshot);
extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment