OSDN Git Service

When using extended-query protocol, postpone planning of unnamed statements
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Jun 2004 01:09:22 +0000 (01:09 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Jun 2004 01:09:22 +0000 (01:09 +0000)
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.

14 files changed:
doc/src/sgml/protocol.sgml
src/backend/commands/explain.c
src/backend/commands/portalcmds.c
src/backend/commands/prepare.c
src/backend/executor/functions.c
src/backend/executor/spi.c
src/backend/optimizer/path/clausesel.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/util/clauses.c
src/backend/tcop/postgres.c
src/backend/utils/adt/selfuncs.c
src/include/optimizer/clauses.h
src/include/optimizer/planner.h
src/include/tcop/tcopprot.h

index de3b727..a88bca3 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
    </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
     destroyed at the end of the transaction, or as soon as the next Bind
index 3658a00..9725028 100644 (file)
@@ -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,
index d2fa894..9cb8feb 100644 (file)
@@ -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
index 083ad2a..9c183cb 100644 (file)
@@ -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,
index 2e75813..9ddf619 100644 (file)
@@ -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)
index f5f2850..91b633d 100644 (file)
@@ -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);
index e736c6e..996c98c 100644 (file)
@@ -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 */
index 064981b..2fc8255 100644 (file)
@@ -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;
 }
index a3a2ddf..e7088d2 100644 (file)
@@ -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"
 
 typedef struct
 {
+       List       *active_fns;
+       bool            estimate;
+} eval_const_expressions_context;
+
+typedef struct
+{
        int                     nargs;
        List       *args;
        int                *usecounts;
@@ -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.
-        */
-       return eval_const_expressions_mutator(node, NIL);
+       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 ...)
+ */
+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;
 
index 07b6096..976b417 100644 (file)
@@ -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);
 
        /*
index 75eb0d2..c7ee847 100644 (file)
@@ -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;
index 09f0822..4d645f4 100644 (file)
@@ -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) (),
index 8688f3d..15440c2 100644 (file)
@@ -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 */
index 1218e04..0f55e97 100644 (file)
@@ -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);