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 de3b72738d752140aa54d9031490cb0aeb48d3e8..a88bca3002610283b43dd431dd425ff2b49df4b3 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>
     </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
index 3658a00ea21f2bf4b4a7e6aa63c11d7f1bcf6cf2..972502895406144dfb88c1666ccbee49d34dccc4 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 d2fa894a76ec4139eb7d11c347bbe445b62b9458..9cb8febd3117bfe54ce4b39156ce1566e5c28ea9 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 083ad2af5e78938ecae0500449f4db120c0397b5..9c183cb7827b193881a6cbfa1e44f4d296278e5d 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 2e75813a2ab97873c8788f963db097dcce6a4e86..9ddf6192775777ff63f11743823c72510bae6bb4 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 f5f2850b9822d88e4fda436a1f62b86ad88147e2..91b633d9bd8400d6f9fd4a481b5e9c62111bae10 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 e736c6e309ecb5f0ac69de86f8fef635338a4a7a..996c98cc469e74e869a39196f0c2c983950ba78e 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 064981b5af0496bfdc3be55b909625f0b0bc69a9..2fc82556c3283f3c9e860b6b78186553dbb7f5f9 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 a3a2ddfbd830a3a858d93e263a484ced584e91d4..e7088d2b7610bea2b450f3e5079497b1b339fba1 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"
 #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.
-    */
-   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 07b60969a45f410216f9b143f440bfb4a39ce2d0..976b417e096fa454da871dc93b40359265550a9d 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 75eb0d2a5299579ec5117c1617d9755763f8bf7f..c7ee847a11a7ee3e995f7b45db9de66678d7ebd3 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 09f0822f468389cf9fd8ae8dccb809f315bfa03c..4d645f43d59efff8f94c66ce88166dd1fe36c621 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 8688f3dd65937486aec176cd0c2d7f0a82485f41..15440c24c4c77d833bfcb47e8c831a80d77fe451 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 1218e04fdfbc821077aca83d1ec1fd2260d0d91b..0f55e9787fdf347c27e3598861c196865e81f5d9 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);