Applied by Tom. Thanks.

---------------------------------------------------------------------------

Oliver Jowett wrote:
> Per discussion on -hackers 
> (http://archives.postgresql.org/pgsql-hackers/2004-05/msg00990.php), 
> here is a first cut at delaying query planning for unnamed statements 
> until we have concrete parameters (i.e. at Bind).
> 
> This passes 'make check' (not particularly informative since that 
> doesn't exercise the extended query protocol path). It also passes the 
> JDBC driver's regression tests when using a modified driver that uses 
> the extended query protocol and parameterized queries.
> 
> Also attached is a python script that talks the V3 protocol directly and 
> does some simple tests of various bind/rebind cases with named and 
> unnamed statements (beware, that code is very ad-hoc!).
> 
> With these changes the JDBC driver gets performance similar to the 
> unparameterized case when using the unnamed statement:
> 
> >  FE=> Parse(stmt=null,query="SELECT count(*) FROM test_big WHERE value = 
> > 2",oids={})
> >  FE=> Bind(stmt=null,portal=null)
> >  FE=> Describe(portal=null)
> >  FE=> Execute(portal=null,limit=0)
> >  FE=> Sync
> >  <=BE ParseComplete [null]
> >  <=BE BindComplete [null]
> >  <=BE RowDescription(1)
> >  <=BE DataRow
> >  <=BE CommandStatus(SELECT)
> >  <=BE ReadyForQuery(I)
> > <<<<< END
> > Elapsed: 54ms
> > Result: 1
> 
> >  FE=> Parse(stmt=S_1,query="SELECT count(*) FROM test_big WHERE value = 
> > $1",oids={23})
> >  FE=> Bind(stmt=S_1,portal=null,$1=<2>)
> >  FE=> Describe(portal=null)
> >  FE=> Execute(portal=null,limit=0)
> >  FE=> Sync
> >  <=BE ParseComplete [S_1]
> >  <=BE BindComplete [null]
> >  <=BE RowDescription(1)
> >  <=BE DataRow
> >  <=BE CommandStatus(SELECT)
> >  <=BE ReadyForQuery(I)
> > <<<<< END
> > Elapsed: 1484ms
> > Result: 1
> 
> >  FE=> Parse(stmt=null,query="SELECT count(*) FROM test_big WHERE value = 
> > $1",oids={23})
> >  FE=> Bind(stmt=null,portal=null,$1=<2>)
> >  FE=> Describe(portal=null)
> >  FE=> Execute(portal=null,limit=0)
> >  FE=> Sync
> >  <=BE ParseComplete [null]
> >  <=BE BindComplete [null]
> >  <=BE RowDescription(1)
> >  <=BE DataRow
> >  <=BE CommandStatus(SELECT)
> >  <=BE ReadyForQuery(I)
> > <<<<< END
> > Elapsed: 65ms
> > Result: 1
> 
> -O

> Index: doc/src/sgml/protocol.sgml
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/protocol.sgml,v
> retrieving revision 1.51
> diff -u -c -r1.51 protocol.sgml
> *** doc/src/sgml/protocol.sgml        21 Mar 2004 22:29:10 -0000      1.51
> --- doc/src/sgml/protocol.sgml        24 May 2004 14:42:10 -0000
> ***************
> *** 663,668 ****
> --- 663,688 ----
>      </para>
>   
>      <para>
> +     Query planning of 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>
> + 
> +    <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.
> +     </para>
> +    </note>
> + 
> +    <para>
>       Once a prepared statement exists, it can be readied for execution using a
>       Bind message.  The Bind message gives the name of the source prepared
>       statement (empty string denotes the unnamed prepared statement), the name
> ***************
> *** 674,679 ****
> --- 694,720 ----
>       by the query; the format can be specified overall, or per-column.
>       The response is either BindComplete or ErrorResponse.
>      </para>
> + 
> +    <para>
> +     Query planning of the unnamed prepared-statement object occurs when the
> +     first Bind message after a Parse message is received. The planner will
> +     consider the actual values of any parameters provided in the Bind message
> +     when planning the query. A Parse followed by Bind of the unnamed
> +     prepared-statement object will produce the same query plan as for the
> +     equivalent unparameterized query.
> +    </para>
> + 
> +    <note>
> +     <para>
> +      When 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 on each execution, send
> +      a Parse message to replace the unnamed prepared-statement object before
> +      each Bind.
> +     </para>
> +    </note>
>   
>      <note>
>       <para>
> Index: src/backend/commands/explain.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/explain.c,v
> retrieving revision 1.120
> diff -u -c -r1.120 explain.c
> *** src/backend/commands/explain.c    1 Apr 2004 21:28:44 -0000       1.120
> --- src/backend/commands/explain.c    24 May 2004 14:42:10 -0000
> ***************
> *** 176,182 ****
>       }
>   
>       /* plan the query */
> !     plan = planner(query, isCursor, cursorOptions);
>   
>       /* Create a QueryDesc requesting no output */
>       queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
> --- 176,182 ----
>       }
>   
>       /* plan the query */
> !     plan = planner(query, isCursor, cursorOptions, NULL);
>   
>       /* Create a QueryDesc requesting no output */
>       queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
> Index: src/backend/commands/portalcmds.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/portalcmds.c,v
> retrieving revision 1.26
> diff -u -c -r1.26 portalcmds.c
> *** src/backend/commands/portalcmds.c 21 Mar 2004 22:29:10 -0000      1.26
> --- src/backend/commands/portalcmds.c 24 May 2004 14:42:10 -0000
> ***************
> *** 84,90 ****
>                                errmsg("DECLARE CURSOR ... FOR UPDATE is not 
> supported"),
>                                errdetail("Cursors must be READ ONLY.")));
>   
> !     plan = planner(query, true, stmt->options);
>   
>       /*
>        * Create a portal and copy the query and plan into its memory
> --- 84,90 ----
>                                errmsg("DECLARE CURSOR ... FOR UPDATE is not 
> supported"),
>                                errdetail("Cursors must be READ ONLY.")));
>   
> !     plan = planner(query, true, stmt->options, NULL);
>   
>       /*
>        * Create a portal and copy the query and plan into its memory
> Index: src/backend/commands/prepare.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/prepare.c,v
> retrieving revision 1.26
> diff -u -c -r1.26 prepare.c
> *** src/backend/commands/prepare.c    22 Apr 2004 02:58:20 -0000      1.26
> --- src/backend/commands/prepare.c    24 May 2004 14:42:10 -0000
> ***************
> *** 91,97 ****
>       query_list = QueryRewrite(stmt->query);
>   
>       /* Generate plans for queries.  Snapshot is already set. */
> !     plan_list = pg_plan_queries(query_list, false);
>   
>       /* Save the results. */
>       StorePreparedStatement(stmt->name,
> --- 91,97 ----
>       query_list = QueryRewrite(stmt->query);
>   
>       /* Generate plans for queries.  Snapshot is already set. */
> !     plan_list = pg_plan_queries(query_list, false, NULL);
>   
>       /* Save the results. */
>       StorePreparedStatement(stmt->name,
> Index: src/backend/executor/functions.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/functions.c,v
> retrieving revision 1.80
> diff -u -c -r1.80 functions.c
> *** src/backend/executor/functions.c  2 Apr 2004 23:14:08 -0000       1.80
> --- src/backend/executor/functions.c  24 May 2004 14:42:10 -0000
> ***************
> *** 100,106 ****
>               Plan       *planTree;
>               execution_state *newes;
>   
> !             planTree = pg_plan_query(queryTree);
>   
>               newes = (execution_state *) palloc(sizeof(execution_state));
>               if (preves)
> --- 100,106 ----
>               Plan       *planTree;
>               execution_state *newes;
>   
> !             planTree = pg_plan_query(queryTree, NULL);
>   
>               newes = (execution_state *) palloc(sizeof(execution_state));
>               if (preves)
> Index: src/backend/executor/spi.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/spi.c,v
> retrieving revision 1.113
> diff -u -c -r1.113 spi.c
> *** src/backend/executor/spi.c        1 Apr 2004 21:28:44 -0000       1.113
> --- src/backend/executor/spi.c        24 May 2004 14:42:10 -0000
> ***************
> *** 1130,1136 ****
>                       QueryDesc  *qdesc;
>                       DestReceiver *dest;
>   
> !                     planTree = pg_plan_query(queryTree);
>                       plan_list = lappend(plan_list, planTree);
>   
>                       dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, 
> NULL);
> --- 1130,1136 ----
>                       QueryDesc  *qdesc;
>                       DestReceiver *dest;
>   
> !                     planTree = pg_plan_query(queryTree, NULL);
>                       plan_list = lappend(plan_list, planTree);
>   
>                       dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, 
> NULL);
> Index: src/backend/optimizer/path/clausesel.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/path/clausesel.c,v
> retrieving revision 1.65
> diff -u -c -r1.65 clausesel.c
> *** src/backend/optimizer/path/clausesel.c    10 May 2004 22:44:45 -0000      1.65
> --- src/backend/optimizer/path/clausesel.c    24 May 2004 14:42:11 -0000
> ***************
> *** 489,496 ****
>       }
>       else if (IsA(clause, Param))
>       {
> !             /* XXX any way to do better? */
> !             s1 = 1.0;
>       }
>       else if (IsA(clause, Const))
>       {
> --- 489,504 ----
>       }
>       else if (IsA(clause, Param))
>       {
> !             /* Try to collapse to Const. */
> !             Node *collapsed_clause = collapse_parameters_to_const(clause);
> !             if (IsA(collapsed_clause, Const)) {
> !                     /* bool constant is pretty easy... */
> !                     s1 = ((bool) ((Const *) collapsed_clause)->constvalue) ? 1.0 : 
> 0.0;
> !             } else {
> !                     /* Can't collapse to Const. */
> !                     /* XXX any way to do better? */
> !                     s1 = 1.0;
> !             }
>       }
>       else if (IsA(clause, Const))
>       {
> Index: src/backend/optimizer/path/indxpath.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/path/indxpath.c,v
> retrieving revision 1.158
> diff -u -c -r1.158 indxpath.c
> *** src/backend/optimizer/path/indxpath.c     27 Mar 2004 00:24:28 -0000      1.158
> --- src/backend/optimizer/path/indxpath.c     24 May 2004 14:42:11 -0000
> ***************
> *** 1068,1073 ****
> --- 1068,1076 ----
>       rightop = get_rightop(predicate);
>       if (rightop == NULL)
>               return false;                   /* not a binary opclause */
> + 
> +     rightop = collapse_parameters_to_const(rightop);
> +     leftop = collapse_parameters_to_const(leftop);
>       if (IsA(rightop, Const))
>       {
>               pred_var = leftop;
> ***************
> *** 1091,1096 ****
> --- 1094,1102 ----
>       rightop = get_rightop((Expr *) clause);
>       if (rightop == NULL)
>               return false;                   /* not a binary opclause */
> + 
> +     rightop = collapse_parameters_to_const(rightop);
> +     leftop = collapse_parameters_to_const(leftop);
>       if (IsA(rightop, Const))
>       {
>               clause_var = leftop;
> ***************
> *** 1873,1878 ****
> --- 1879,1885 ----
>       expr_op = ((OpExpr *) clause)->opno;
>   
>       /* again, required for all current special ops: */
> +     rightop = collapse_parameters_to_const(rightop);
>       if (!IsA(rightop, Const) ||
>               ((Const *) rightop)->constisnull)
>               return false;
> ***************
> *** 2056,2062 ****
>       Node       *leftop = get_leftop(clause);
>       Node       *rightop = get_rightop(clause);
>       Oid                     expr_op = ((OpExpr *) clause)->opno;
> !     Const      *patt = (Const *) rightop;
>       Const      *prefix = NULL;
>       Const      *rest = NULL;
>       Pattern_Prefix_Status pstatus;
> --- 2063,2069 ----
>       Node       *leftop = get_leftop(clause);
>       Node       *rightop = get_rightop(clause);
>       Oid                     expr_op = ((OpExpr *) clause)->opno;
> !     Const      *patt = (Const *) collapse_parameters_to_const(rightop);
>       Const      *prefix = NULL;
>       Const      *rest = NULL;
>       Pattern_Prefix_Status pstatus;
> Index: src/backend/optimizer/plan/createplan.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/plan/createplan.c,v
> retrieving revision 1.169
> diff -u -c -r1.169 createplan.c
> *** src/backend/optimizer/plan/createplan.c   25 Apr 2004 18:23:56 -0000      1.169
> --- src/backend/optimizer/plan/createplan.c   24 May 2004 14:42:11 -0000
> ***************
> *** 2330,2335 ****
> --- 2330,2337 ----
>        * level, but if we are building a subquery then it's important to
>        * report correct info to the outer planner.
>        */
> +     if (limitOffset)
> +             limitOffset = collapse_parameters_to_const(limitOffset);
>       if (limitOffset && IsA(limitOffset, Const))
>       {
>               Const      *limito = (Const *) limitOffset;
> ***************
> *** 2348,2353 ****
> --- 2350,2357 ----
>                               plan->plan_rows = 1;
>               }
>       }
> +     if (limitCount)
> +             limitCount = collapse_parameters_to_const(limitCount);
>       if (limitCount && IsA(limitCount, Const))
>       {
>               Const      *limitc = (Const *) limitCount;
> Index: src/backend/optimizer/plan/planner.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/plan/planner.c,v
> retrieving revision 1.169
> diff -u -c -r1.169 planner.c
> *** src/backend/optimizer/plan/planner.c      11 May 2004 02:21:37 -0000      1.169
> --- src/backend/optimizer/plan/planner.c      24 May 2004 14:42:11 -0000
> ***************
> *** 71,82 ****
>    *
>    *****************************************************************************/
>   Plan *
> ! planner(Query *parse, bool isCursor, int cursorOptions)
>   {
>       double          tuple_fraction;
>       Plan       *result_plan;
>       Index           save_PlannerQueryLevel;
>       List       *save_PlannerParamList;
>   
>       /*
>        * The planner can be called recursively (an example is when
> --- 71,83 ----
>    *
>    *****************************************************************************/
>   Plan *
> ! 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
> ***************
> *** 93,102 ****
> --- 94,105 ----
>        */
>       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,144 ****
> --- 142,148 ----
>       /* restore state for outer planner, if any */
>       PlannerQueryLevel = save_PlannerQueryLevel;
>       PlannerParamList = save_PlannerParamList;
> +     PlannerBoundParamList = save_PlannerBoundParamList;
>   
>       return result_plan;
>   }
> ***************
> *** 401,407 ****
>       /*
>        * Simplify constant expressions.
>        */
> !     expr = eval_const_expressions(expr);
>   
>       /* Expand SubLinks to SubPlans */
>       if (parse->hasSubLinks)
> --- 405,411 ----
>       /*
>        * Simplify constant expressions.
>        */
> !     expr = eval_const_expressions(expr, NULL);
>   
>       /* Expand SubLinks to SubPlans */
>       if (parse->hasSubLinks)
> ***************
> *** 762,770 ****
>                        */
>                       double          limit_fraction = 0.0;
>   
> !                     if (IsA(parse->limitCount, Const))
>                       {
> !                             Const      *limitc = (Const *) parse->limitCount;
>                               int32           count = 
> DatumGetInt32(limitc->constvalue);
>   
>                               /*
> --- 766,775 ----
>                        */
>                       double          limit_fraction = 0.0;
>   
> !                     Node *limitCount = 
> collapse_parameters_to_const(parse->limitCount);
> !                     if (IsA(limitCount, Const))
>                       {
> !                             Const      *limitc = (Const *) limitCount;
>                               int32           count = 
> DatumGetInt32(limitc->constvalue);
>   
>                               /*
> ***************
> *** 778,788 ****
>                                       /* We must also consider the OFFSET, if 
> present */
>                                       if (parse->limitOffset != NULL)
>                                       {
> !                                             if (IsA(parse->limitOffset, Const))
>                                               {
>                                                       int32           offset;
>   
> !                                                     limitc = (Const *) 
> parse->limitOffset;
>                                                       offset = 
> DatumGetInt32(limitc->constvalue);
>                                                       if (!limitc->constisnull && 
> offset > 0)
>                                                               limit_fraction += 
> (double) offset;
> --- 783,794 ----
>                                       /* We must also consider the OFFSET, if 
> present */
>                                       if (parse->limitOffset != NULL)
>                                       {
> !                                             Node *limitOffset = 
> collapse_parameters_to_const(parse->limitCount);
> !                                             if (IsA(limitOffset, Const))
>                                               {
>                                                       int32           offset;
>   
> !                                                     limitc = (Const *) limitOffset;
>                                                       offset = 
> DatumGetInt32(limitc->constvalue);
>                                                       if (!limitc->constisnull && 
> offset > 0)
>                                                               limit_fraction += 
> (double) offset;
> Index: src/backend/optimizer/prep/prepunion.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/prep/prepunion.c,v
> retrieving revision 1.110
> diff -u -c -r1.110 prepunion.c
> *** src/backend/optimizer/prep/prepunion.c    11 May 2004 22:43:55 -0000      1.110
> --- src/backend/optimizer/prep/prepunion.c    24 May 2004 14:42:11 -0000
> ***************
> *** 430,435 ****
> --- 430,436 ----
>               TargetEntry *inputtle = (TargetEntry *) lfirst(input_tlist);
>               TargetEntry *reftle = (TargetEntry *) lfirst(refnames_tlist);
>               int32           colTypmod;
> +             Node        *collapsed_expr;
>   
>               Assert(inputtle->resdom->resno == resno);
>               Assert(reftle->resdom->resno == resno);
> ***************
> *** 449,456 ****
>                * subquery-scan plans; we don't want phony constants appearing in
>                * the output tlists of upper-level nodes!
>                */
> !             if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
> !                     expr = (Node *) inputtle->expr;
>               else
>                       expr = (Node *) makeVar(0,
>                                                                       
> inputtle->resdom->resno,
> --- 450,459 ----
>                * subquery-scan plans; we don't want phony constants appearing in
>                * the output tlists of upper-level nodes!
>                */
> !             if (hack_constants && inputtle->expr &&
> !                     NULL != (collapsed_expr = collapse_parameters_to_const((Node 
> *) inputtle->expr)) &&
> !                     IsA(collapsed_expr, Const))
> !                     expr = collapsed_expr;
>               else
>                       expr = (Node *) makeVar(0,
>                                                                       
> inputtle->resdom->resno,
> Index: src/backend/optimizer/util/clauses.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/util/clauses.c,v
> retrieving revision 1.170
> diff -u -c -r1.170 clauses.c
> *** src/backend/optimizer/util/clauses.c      10 May 2004 22:44:45 -0000      1.170
> --- src/backend/optimizer/util/clauses.c      24 May 2004 14:42:12 -0000
> ***************
> *** 28,37 ****
> --- 28,39 ----
>   #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 "parser/parse_expr.h"
> + #include "parser/parse_type.h"
>   #include "tcop/tcopprot.h"
>   #include "utils/acl.h"
>   #include "utils/builtins.h"
> ***************
> *** 41,48 ****
> --- 43,61 ----
>   #include "utils/syscache.h"
>   
>   
> + ParamListInfo PlannerBoundParamList = NULL; /* to keep track of currently-bound 
> parameter values */
> + 
> + 
> + typedef struct
> + {
> +     List *active_fns;
> +     ParamListInfo params;
> +     int nparams;
> + } eval_const_expressions_context;
> + 
>   typedef struct
>   {
> +     
>       int                     nargs;
>       List       *args;
>       int                *usecounts;
> ***************
> *** 57,73 ****
>   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 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);
>   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);
>   static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
>                                                        int *usecounts);
>   static Node *substitute_actual_parameters_mutator(Node *node,
> --- 70,86 ----
>   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, 
> 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, eval_const_expressions_context 
> *active_fns);
>   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, eval_const_expressions_context 
> *active_fns);
>   static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
>                                                        int *usecounts);
>   static Node *substitute_actual_parameters_mutator(Node *node,
> ***************
> *** 1062,1078 ****
>    *--------------------
>    */
>   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);
>   }
>   
>   static Node *
> ! eval_const_expressions_mutator(Node *node, List *active_fns)
>   {
>       if (node == NULL)
>               return NULL;
> --- 1075,1128 ----
>    *--------------------
>    */
>   Node *
> ! eval_const_expressions(Node *node, ParamListInfo params)
>   {
>       /*
> !      * The context for the mutator is the parameter list plus
> !      * a list of SQL functions being recursively simplified.
> !      * The function list is initially empty.
>        */
> !     int i;
> !     eval_const_expressions_context context;
> !     context.active_fns = NIL;
> !     context.params = params;
> ! 
> !     if (params != NULL) {
> !             /* Count and check parameters. */
> !             for (i = 0; params[i].kind != PARAM_INVALID; ++i) {
> !                     if (params[i].id != (i + 1))
> !                             elog(ERROR, "param id mismatch: expected %d but was 
> %d", i+1, params[i].id);
> !             }
> !     
> !             context.nparams = i;
> !     } else {
> !             context.nparams = 0;
> !     }
> ! 
> !     return eval_const_expressions_mutator(node, &context);
> ! }
> ! 
> ! /*
> !  * Helper for selectivity functions: apply a Param to Const replacement,
> !  * using parameters from PlannerBoundParamList, and return the new tree. Might
> !  * not copy the tree if it's obvious no change will happen.
> !  */
> ! Node *
> ! collapse_parameters_to_const(Node *node)
> ! {
> !     /* Already a leaf? */
> !     if (IsA(node, Const) || IsA(node, Var))
> !             return node;
> ! 
> !     /* No parameters? */
> !     if (PlannerBoundParamList == NULL || PlannerBoundParamList[0].kind == 
> PARAM_INVALID)
> !             return node;
> ! 
> !     return eval_const_expressions(node, PlannerBoundParamList);
>   }
>   
>   static Node *
> ! eval_const_expressions_mutator(Node *node, eval_const_expressions_context *context)
>   {
>       if (node == NULL)
>               return NULL;
> ***************
> *** 1090,1103 ****
>                */
>               args = (List *) expression_tree_mutator((Node *) expr->args,
>                                                                                 
> eval_const_expressions_mutator,
> !                                                                                    
>          (void *) active_fns);
>   
>               /*
>                * 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);
>               if (simple)                             /* successfully simplified it 
> */
>                       return (Node *) simple;
>   
> --- 1140,1153 ----
>                */
>               args = (List *) expression_tree_mutator((Node *) expr->args,
>                                                                                 
> eval_const_expressions_mutator,
> !                                                                                    
>          (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, context);
>               if (simple)                             /* successfully simplified it 
> */
>                       return (Node *) simple;
>   
> ***************
> *** 1128,1134 ****
>                */
>               args = (List *) expression_tree_mutator((Node *) expr->args,
>                                                                                 
> eval_const_expressions_mutator,
> !                                                                                    
>          (void *) active_fns);
>   
>               /*
>                * Need to get OID of underlying function.      Okay to scribble on
> --- 1178,1184 ----
>                */
>               args = (List *) expression_tree_mutator((Node *) expr->args,
>                                                                                 
> eval_const_expressions_mutator,
> !                                                                                    
>          (void *) context);
>   
>               /*
>                * Need to get OID of underlying function.      Okay to scribble on
> ***************
> *** 1141,1147 ****
>                * a separate function.
>                */
>               simple = simplify_function(expr->opfuncid, expr->opresulttype, args,
> !                                                                true, active_fns);
>               if (simple)                             /* successfully simplified it 
> */
>                       return (Node *) simple;
>   
> --- 1191,1197 ----
>                * a separate function.
>                */
>               simple = simplify_function(expr->opfuncid, expr->opresulttype, args,
> !                                                                true, context);
>               if (simple)                             /* successfully simplified it 
> */
>                       return (Node *) simple;
>   
> ***************
> *** 1176,1182 ****
>                */
>               args = (List *) expression_tree_mutator((Node *) expr->args,
>                                                                                 
> eval_const_expressions_mutator,
> !                                                                                    
>          (void *) active_fns);
>   
>               /*
>                * We must do our own check for NULLs because DistinctExpr has
> --- 1226,1232 ----
>                */
>               args = (List *) expression_tree_mutator((Node *) expr->args,
>                                                                                 
> eval_const_expressions_mutator,
> !                                                                                    
>          (void *) context);
>   
>               /*
>                * We must do our own check for NULLs because DistinctExpr has
> ***************
> *** 1220,1226 ****
>                        * as a separate function.
>                        */
>                       simple = simplify_function(expr->opfuncid, expr->opresulttype,
> !                                                                        args, 
> false, active_fns);
>                       if (simple)                     /* successfully simplified it 
> */
>                       {
>                               /*
> --- 1270,1276 ----
>                        * as a separate function.
>                        */
>                       simple = simplify_function(expr->opfuncid, expr->opresulttype,
> !                                                                        args, 
> false, context);
>                       if (simple)                     /* successfully simplified it 
> */
>                       {
>                               /*
> ***************
> *** 1261,1267 ****
>                */
>               args = (List *) expression_tree_mutator((Node *) expr->args,
>                                                                                 
> eval_const_expressions_mutator,
> !                                                                                    
>          (void *) active_fns);
>   
>               switch (expr->boolop)
>               {
> --- 1311,1317 ----
>                */
>               args = (List *) expression_tree_mutator((Node *) expr->args,
>                                                                                 
> eval_const_expressions_mutator,
> !                                                                                    
>          (void *) context);
>   
>               switch (expr->boolop)
>               {
> ***************
> *** 1354,1360 ****
>               Node       *arg;
>   
>               arg = eval_const_expressions_mutator((Node *) relabel->arg,
> !                                                                                    
>   active_fns);
>   
>               /*
>                * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we
> --- 1404,1410 ----
>               Node       *arg;
>   
>               arg = eval_const_expressions_mutator((Node *) relabel->arg,
> !                                                                                    
>   context);
>   
>               /*
>                * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we
> ***************
> *** 1418,1424 ****
>   
>               /* Simplify the test expression, if any */
>               newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
> !                                                                                    
>          active_fns);
>   
>               /* Simplify the WHEN clauses */
>               FastListInit(&newargs);
> --- 1468,1474 ----
>   
>               /* Simplify the test expression, if any */
>               newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
> !                                                                                    
>          context);
>   
>               /* Simplify the WHEN clauses */
>               FastListInit(&newargs);
> ***************
> *** 1428,1434 ****
>                       CaseWhen   *casewhen = (CaseWhen *)
>                       expression_tree_mutator((Node *) lfirst(arg),
>                                                                       
> eval_const_expressions_mutator,
> !                                                                     (void *) 
> active_fns);
>   
>                       Assert(IsA(casewhen, CaseWhen));
>                       if (casewhen->expr == NULL ||
> --- 1478,1484 ----
>                       CaseWhen   *casewhen = (CaseWhen *)
>                       expression_tree_mutator((Node *) lfirst(arg),
>                                                                       
> eval_const_expressions_mutator,
> !                                                                     (void *) 
> context);
>   
>                       Assert(IsA(casewhen, CaseWhen));
>                       if (casewhen->expr == NULL ||
> ***************
> *** 1458,1464 ****
>   
>               /* Simplify the default result */
>               defresult = eval_const_expressions_mutator((Node *) 
> caseexpr->defresult,
> !                                                                                    
>             active_fns);
>   
>               /*
>                * If no non-FALSE alternatives, CASE reduces to the default
> --- 1508,1514 ----
>   
>               /* Simplify the default result */
>               defresult = eval_const_expressions_mutator((Node *) 
> caseexpr->defresult,
> !                                                                                    
>             context);
>   
>               /*
>                * If no non-FALSE alternatives, CASE reduces to the default
> ***************
> *** 1488,1494 ****
>                       Node       *e;
>   
>                       e = eval_const_expressions_mutator((Node *) lfirst(element),
> !                                                                                    
>     active_fns);
>                       if (!IsA(e, Const))
>                               all_const = false;
>                       FastAppend(&newelems, e);
> --- 1538,1544 ----
>                       Node       *e;
>   
>                       e = eval_const_expressions_mutator((Node *) lfirst(element),
> !                                                                                    
>     context);
>                       if (!IsA(e, Const))
>                               all_const = false;
>                       FastAppend(&newelems, e);
> ***************
> *** 1519,1525 ****
>                       Node       *e;
>   
>                       e = eval_const_expressions_mutator((Node *) lfirst(arg),
> !                                                                                    
>     active_fns);
>   
>                       /*
>                        * We can remove null constants from the list. For a non-null
> --- 1569,1575 ----
>                       Node       *e;
>   
>                       e = eval_const_expressions_mutator((Node *) lfirst(arg),
> !                                                                                    
>     context);
>   
>                       /*
>                        * We can remove null constants from the list. For a non-null
> ***************
> *** 1555,1561 ****
>               Node       *arg;
>   
>               arg = eval_const_expressions_mutator((Node *) fselect->arg,
> !                                                                                    
>   active_fns);
>               if (arg && IsA(arg, Var) &&
>                       ((Var *) arg)->varattno == InvalidAttrNumber)
>               {
> --- 1605,1611 ----
>               Node       *arg;
>   
>               arg = eval_const_expressions_mutator((Node *) fselect->arg,
> !                                                                                    
>   context);
>               if (arg && IsA(arg, Var) &&
>                       ((Var *) arg)->varattno == InvalidAttrNumber)
>               {
> ***************
> *** 1580,1585 ****
> --- 1630,1665 ----
>               newfselect->resulttypmod = fselect->resulttypmod;
>               return (Node *) newfselect;
>       }
> +     if (IsA(node, Param))
> +     {
> +             /*
> +              * Attempt to substitute concrete parameter values in, if we have them.
> +              */
> + 
> +             Param *param = (Param *) node;
> + 
> +             if (param->paramkind == PARAM_NUM && context->params != NULL) {
> +                     /*
> +                      * Return a Constant in place of this Param.
> +                      */
> + 
> +                     Type type;
> +                     Const *replacement;
> + 
> +                     if (param->paramid <= 0 || param->paramid > context->nparams)
> +                             elog(ERROR, "invalid paramid: %d", param->paramid);
> +                     
> +                     type = typeidType(param->paramtype);                    
> +                     replacement = makeConst(param->paramtype,
> +                                                                     typeLen(type),
> +                                                                     
> context->params[param->paramid - 1].value,
> +                                                                     
> context->params[param->paramid - 1].isnull,
> +                                                                     
> typeByVal(type));
> +                     
> +                     ReleaseSysCache(type);
> +                     return (Node *) replacement;
> +             }
> +     }
>   
>       /*
>        * For any node type not handled above, we recurse using
> ***************
> *** 1589,1595 ****
>        * simplify constant expressions in its subscripts.
>        */
>       return expression_tree_mutator(node, eval_const_expressions_mutator,
> !                                                                (void *) 
> active_fns);
>   }
>   
>   /*
> --- 1669,1675 ----
>        * simplify constant expressions in its subscripts.
>        */
>       return expression_tree_mutator(node, eval_const_expressions_mutator,
> !                                                                (void *) context);
>   }
>   
>   /*
> ***************
> *** 1727,1733 ****
>    */
>   static Expr *
>   simplify_function(Oid funcid, Oid result_type, List *args,
> !                               bool allow_inline, List *active_fns)
>   {
>       HeapTuple       func_tuple;
>       Expr       *newexpr;
> --- 1807,1813 ----
>    */
>   static Expr *
>   simplify_function(Oid funcid, Oid result_type, List *args,
> !                               bool allow_inline, eval_const_expressions_context 
> *context)
>   {
>       HeapTuple       func_tuple;
>       Expr       *newexpr;
> ***************
> *** 1750,1756 ****
>   
>       if (!newexpr && allow_inline)
>               newexpr = inline_function(funcid, result_type, args,
> !                                                               func_tuple, 
> active_fns);
>   
>       ReleaseSysCache(func_tuple);
>   
> --- 1830,1836 ----
>   
>       if (!newexpr && allow_inline)
>               newexpr = inline_function(funcid, result_type, args,
> !                                                               func_tuple, context);
>   
>       ReleaseSysCache(func_tuple);
>   
> ***************
> *** 1854,1860 ****
>    */
>   static Expr *
>   inline_function(Oid funcid, Oid result_type, List *args,
> !                             HeapTuple func_tuple, List *active_fns)
>   {
>       Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
>       bool            polymorphic = false;
> --- 1934,1940 ----
>    */
>   static Expr *
>   inline_function(Oid funcid, Oid result_type, List *args,
> !                             HeapTuple func_tuple, eval_const_expressions_context 
> *context)
>   {
>       Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
>       bool            polymorphic = false;
> ***************
> *** 1884,1890 ****
>               return NULL;
>   
>       /* Check for recursive function, and give up trying to expand if so */
> !     if (oidMember(funcid, active_fns))
>               return NULL;
>   
>       /* Check permission to call function (fail later, if not) */
> --- 1964,1970 ----
>               return NULL;
>   
>       /* Check for recursive function, and give up trying to expand if so */
> !     if (oidMember(funcid, context->active_fns))
>               return NULL;
>   
>       /* Check permission to call function (fail later, if not) */
> ***************
> *** 2077,2084 ****
>        * 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,
> !                                                                                    
>   lconso(funcid, active_fns));
>   
>       error_context_stack = sqlerrcontext.previous;
>   
> --- 2157,2164 ----
>        * Recursively try to simplify the modified expression.  Here we must
>        * add the current function to the context list of active functions.
>        */
> !     context->active_fns = lconso(funcid, context->active_fns);
> !     newexpr = eval_const_expressions_mutator(newexpr, context);
>   
>       error_context_stack = sqlerrcontext.previous;
>   
> Index: src/backend/tcop/postgres.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/tcop/postgres.c,v
> retrieving revision 1.414
> diff -u -c -r1.414 postgres.c
> *** src/backend/tcop/postgres.c       23 May 2004 03:50:45 -0000      1.414
> --- src/backend/tcop/postgres.c       24 May 2004 14:42:12 -0000
> ***************
> *** 631,637 ****
>   
>   /* Generate a plan for a single already-rewritten query. */
>   Plan *
> ! pg_plan_query(Query *querytree)
>   {
>       Plan       *plan;
>   
> --- 631,637 ----
>   
>   /* Generate a plan for a single already-rewritten query. */
>   Plan *
> ! pg_plan_query(Query *querytree, ParamListInfo params)
>   {
>       Plan       *plan;
>   
> ***************
> *** 643,649 ****
>               ResetUsage();
>   
>       /* call the optimizer */
> !     plan = planner(querytree, false, 0);
>   
>       if (log_planner_stats)
>               ShowUsage("PLANNER STATISTICS");
> --- 643,649 ----
>               ResetUsage();
>   
>       /* call the optimizer */
> !     plan = planner(querytree, false, 0, params);
>   
>       if (log_planner_stats)
>               ShowUsage("PLANNER STATISTICS");
> ***************
> *** 688,694 ****
>    * statements in the rewriter's output.)
>    */
>   List *
> ! pg_plan_queries(List *querytrees, bool needSnapshot)
>   {
>       List       *plan_list = NIL;
>       List       *query_list;
> --- 688,694 ----
>    * statements in the rewriter's output.)
>    */
>   List *
> ! pg_plan_queries(List *querytrees, bool needSnapshot, ParamListInfo params)
>   {
>       List       *plan_list = NIL;
>       List       *query_list;
> ***************
> *** 710,716 ****
>                               SetQuerySnapshot();
>                               needSnapshot = false;
>                       }
> !                     plan = pg_plan_query(query);
>               }
>   
>               plan_list = lappend(plan_list, plan);
> --- 710,716 ----
>                               SetQuerySnapshot();
>                               needSnapshot = false;
>                       }
> !                     plan = pg_plan_query(query, params);
>               }
>   
>               plan_list = lappend(plan_list, plan);
> ***************
> *** 868,874 ****
>   
>               querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0);
>   
> !             plantree_list = pg_plan_queries(querytree_list, true);
>   
>               /* If we got a cancel signal in analysis or planning, quit */
>               CHECK_FOR_INTERRUPTS();
> --- 868,874 ----
>   
>               querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0);
>   
> !             plantree_list = pg_plan_queries(querytree_list, true, NULL);
>   
>               /* If we got a cancel signal in analysis or planning, quit */
>               CHECK_FOR_INTERRUPTS();
> ***************
> *** 1206,1212 ****
>   
>               querytree_list = pg_rewrite_queries(querytree_list);
>   
> !             plantree_list = pg_plan_queries(querytree_list, true);
>       }
>       else
>       {
> --- 1206,1217 ----
>   
>               querytree_list = pg_rewrite_queries(querytree_list);
>   
> !             /* If this is the unnamed statement and we have parameters, defer 
> query planning until Bind. */
> !             if (!is_named && numParams > 0) {
> !                     plantree_list = NIL;
> !             } else {
> !                     plantree_list = pg_plan_queries(querytree_list, true, NULL);
> !             }
>       }
>       else
>       {
> ***************
> *** 1357,1368 ****
>       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.
> --- 1362,1368 ----
>       else
>               portal = CreatePortal(portal_name, false, false);
>   
> !     /* Defer portal query definition until we're sure planning is done. */
>   
>       /*
>        * Fetch parameters, if any, and store in the portal's memory context.
> ***************
> *** 1517,1524 ****
>       pq_getmsgend(input_message);
>   
>       /*
> !      * Start portal execution.
>        */
>       PortalStart(portal, params);
>   
>       /*
> --- 1517,1542 ----
>       pq_getmsgend(input_message);
>   
>       /*
> !      * If this is the unnamed statement, we may not have planned the
> !      * query yet. In that case, do the planning (in the query's
> !      * memory context) now that we have concrete parameter values.
> !      */
> !     if (pstmt->plan_list == NIL && pstmt->query_list != NIL) {
> !             MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context);
> !             pstmt->plan_list = pg_plan_queries(pstmt->query_list, true, params);
> !             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: src/backend/utils/adt/selfuncs.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/selfuncs.c,v
> retrieving revision 1.158
> diff -u -c -r1.158 selfuncs.c
> *** src/backend/utils/adt/selfuncs.c  27 Feb 2004 21:44:34 -0000      1.158
> --- src/backend/utils/adt/selfuncs.c  24 May 2004 14:42:12 -0000
> ***************
> *** 242,247 ****
> --- 242,249 ----
>                                                                 &vardata, &other, 
> &varonleft))
>               PG_RETURN_FLOAT8(DEFAULT_EQ_SEL);
>   
> +     other = collapse_parameters_to_const(other);
> + 
>       /*
>        * If the something is a NULL constant, assume operator is strict and
>        * return zero, ie, operator will never return TRUE.
> ***************
> *** 711,716 ****
> --- 713,720 ----
>                                                                 &vardata, &other, 
> &varonleft))
>               PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
>   
> +     other = collapse_parameters_to_const(other);
> + 
>       /*
>        * Can't do anything useful if the something is not a constant,
>        * either.
> ***************
> *** 787,792 ****
> --- 791,798 ----
>                                                                 &vardata, &other, 
> &varonleft))
>               PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
>   
> +     other = collapse_parameters_to_const(other);
> + 
>       /*
>        * Can't do anything useful if the something is not a constant,
>        * either.
> ***************
> *** 870,876 ****
>       if (!get_restriction_variable(root, args, varRelid,
>                                                                 &vardata, &other, 
> &varonleft))
>               return DEFAULT_MATCH_SEL;
> !     if (!varonleft || !IsA(other, Const))
>       {
>               ReleaseVariableStats(vardata);
>               return DEFAULT_MATCH_SEL;
> --- 876,891 ----
>       if (!get_restriction_variable(root, args, varRelid,
>                                                                 &vardata, &other, 
> &varonleft))
>               return DEFAULT_MATCH_SEL;
> ! 
> !     if (!varonleft)
> !     {
> !             ReleaseVariableStats(vardata);
> !             return DEFAULT_MATCH_SEL;
> !     }
> ! 
> !     other = collapse_parameters_to_const(other);
> ! 
> !     if (!IsA(other, Const))
>       {
>               ReleaseVariableStats(vardata);
>               return DEFAULT_MATCH_SEL;
> Index: src/backend/utils/cache/relcache.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/cache/relcache.c,v
> retrieving revision 1.202
> diff -u -c -r1.202 relcache.c
> *** src/backend/utils/cache/relcache.c        8 May 2004 19:09:25 -0000       1.202
> --- src/backend/utils/cache/relcache.c        24 May 2004 14:42:13 -0000
> ***************
> *** 2680,2686 ****
>        */
>       result = (List *) flatten_andors((Node *) result);
>   
> !     result = (List *) eval_const_expressions((Node *) result);
>   
>       /*
>        * Also mark any coercion format fields as "don't care", so that the
> --- 2680,2686 ----
>        */
>       result = (List *) flatten_andors((Node *) result);
>   
> !     result = (List *) eval_const_expressions((Node *) result, NULL);
>   
>       /*
>        * Also mark any coercion format fields as "don't care", so that the
> ***************
> *** 2754,2760 ****
>        */
>       result = (List *) canonicalize_qual((Expr *) result);
>   
> !     result = (List *) eval_const_expressions((Node *) result);
>   
>       /*
>        * Also mark any coercion format fields as "don't care", so that the
> --- 2754,2760 ----
>        */
>       result = (List *) canonicalize_qual((Expr *) result);
>   
> !     result = (List *) eval_const_expressions((Node *) result, NULL);
>   
>       /*
>        * Also mark any coercion format fields as "don't care", so that the
> Index: src/include/optimizer/clauses.h
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/include/optimizer/clauses.h,v
> retrieving revision 1.73
> diff -u -c -r1.73 clauses.h
> *** src/include/optimizer/clauses.h   14 Mar 2004 23:41:27 -0000      1.73
> --- src/include/optimizer/clauses.h   24 May 2004 14:42:13 -0000
> ***************
> *** 15,22 ****
>   #define CLAUSES_H
>   
>   #include "nodes/relation.h"
>   
> ! 
>   
>   #define is_opclause(clause)         ((clause) != NULL && IsA(clause, OpExpr))
>   #define is_funcclause(clause)       ((clause) != NULL && IsA(clause, FuncExpr))
> --- 15,23 ----
>   #define CLAUSES_H
>   
>   #include "nodes/relation.h"
> + #include "nodes/params.h"
>   
> ! extern ParamListInfo PlannerBoundParamList; /* to keep track of 
> externally-specified parameter values */
>   
>   #define is_opclause(clause)         ((clause) != NULL && IsA(clause, OpExpr))
>   #define is_funcclause(clause)       ((clause) != NULL && IsA(clause, FuncExpr))
> ***************
> *** 65,71 ****
>   
>   extern void set_coercionform_dontcare(Node *node);
>   
> ! extern Node *eval_const_expressions(Node *node);
>   
>   extern bool expression_tree_walker(Node *node, bool (*walker) (),
>                                                                                      
>     void *context);
> --- 66,74 ----
>   
>   extern void set_coercionform_dontcare(Node *node);
>   
> ! extern Node *eval_const_expressions(Node *node, ParamListInfo params);
> ! 
> ! extern Node *collapse_parameters_to_const(Node *node);
>   
>   extern bool expression_tree_walker(Node *node, bool (*walker) (),
>                                                                                      
>     void *context);
> Index: src/include/optimizer/planner.h
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/include/optimizer/planner.h,v
> retrieving revision 1.28
> diff -u -c -r1.28 planner.h
> *** src/include/optimizer/planner.h   29 Nov 2003 22:41:07 -0000      1.28
> --- src/include/optimizer/planner.h   24 May 2004 14:42:13 -0000
> ***************
> *** 16,24 ****
>   
>   #include "nodes/parsenodes.h"
>   #include "nodes/plannodes.h"
>   
> ! 
> ! extern Plan *planner(Query *parse, bool isCursor, int cursorOptions);
>   extern Plan *subquery_planner(Query *parse, double tuple_fraction);
>   
>   #endif   /* PLANNER_H */
> --- 16,24 ----
>   
>   #include "nodes/parsenodes.h"
>   #include "nodes/plannodes.h"
> + #include "nodes/params.h"
>   
> ! extern Plan *planner(Query *parse, bool isCursor, int cursorOptions, ParamListInfo 
> extParams);
>   extern Plan *subquery_planner(Query *parse, double tuple_fraction);
>   
>   #endif   /* PLANNER_H */
> Index: src/include/tcop/tcopprot.h
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/include/tcop/tcopprot.h,v
> retrieving revision 1.65
> diff -u -c -r1.65 tcopprot.h
> *** src/include/tcop/tcopprot.h       11 Apr 2004 00:54:45 -0000      1.65
> --- src/include/tcop/tcopprot.h       24 May 2004 14:42:13 -0000
> ***************
> *** 24,29 ****
> --- 24,30 ----
>   #include "executor/execdesc.h"
>   #include "tcop/dest.h"
>   #include "utils/guc.h"
> + #include "nodes/params.h"
>   
>   
>   extern DLLIMPORT sigjmp_buf Warn_restart;
> ***************
> *** 57,64 ****
>   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 bool assign_max_stack_depth(int newval, bool doit, GucSource source);
>   
> --- 58,65 ----
>   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, ParamListInfo params);
> ! extern List *pg_plan_queries(List *querytrees, bool needSnapshot, ParamListInfo 
> params);
>   
>   extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);
>   

[ text/x-python is unsupported, treating like TEXT/PLAIN ]

> #!/usr/bin/env python
> 
> import sys, socket, struct
> 
> # V3 primitives
> 
> def send_startup(conn, db, user):
>     paramValues = 'user\0%s\0database\0%s\0\0' % (user, db)
>     msgLen = 8 + len(paramValues)
>     msg = struct.pack('>II', msgLen, 196608) + paramValues
>     conn.send(msg)
> 
> def send(conn, type, data):
>     msg = type + struct.pack('>i', len(data)+4) + data
>     conn.send(msg)
> 
> def send_parse(conn, statement, query, oids):
>     sys.stdout.write('<= Parse(%s,%s,%s)\n' % (repr(statement), repr(query), 
> repr(oids)))
>     msg = statement + '\0'
>     msg += query + '\0'
>     msg += struct.pack('>H', len(oids))
>     for oid in oids:
>         msg += struct.pack('>I', oid)
>     send(conn, 'P', msg)
> 
> def send_bind(conn, portal, statement, params):
>     sys.stdout.write('<= Bind(%s,%s,%s)\n' % (repr(portal), repr(statement), 
> repr(params)))
>     msg = portal + '\0'
>     msg += statement + '\0'
>     msg += struct.pack('>H', 0)           # param format count
>     msg += struct.pack('>H', len(params)) # param count
>     for param in params:
>         if param is None:
>             msg += struct.pack('>i', -1)  # NULL
>         else:
>             msg += struct.pack('>i', len(param) + 1) # param data length
>             msg += param + '\0'
>     msg += struct.pack('>H', 0)           # result format count
>     send(conn, 'B', msg)
> 
> def send_execute(conn, portal, count):
>     sys.stdout.write('<= Execute(%s,%d)\n' % (repr(portal), count))
>     msg = portal + '\0'
>     msg += struct.pack('>i', 0)  # max rows
>     send(conn, 'E', msg)
> 
> def send_sync(conn):
>     sys.stdout.write('<= Sync\n')
>     send(conn, 'S', '')
> 
> #
> # Connection setup.
> #
> 
> def connect(host, port, db, user):
>     conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
>     conn.connect( (host, port) )
>     send_startup(conn, db, user)
> 
>     type = conn.recv(1)
>     if type != 'R': raise RuntimeError('bad startup type: ' + type)
>     
>     length, = struct.unpack('>i', conn.recv(4))
>     data = conn.recv(length-4)
>     res, = struct.unpack('>i', data[:4])
>     
>     if res != 0: raise RuntimeError("auth was needed but we don't do it")
> 
>     process_results(conn)
>     return conn
> 
> #
> # Normal connection processing
> #
> 
> def make_log(type):
>     return lambda conn, data: sys.stdout.write('  => ' + type + '\n')
> 
> def make_server_response(type):
>     def server_response(conn, data, type=type):
>         sys.stdout.write('  => ' + type + '\n')
>         i = 0
>         while i != -1 and data[i] != '\0':
>             type = data[i]
>             end = data.find('\0', i+1)
>             if end == -1:
>                 value = data[i+1:]
>                 i = -1
>             else:
>                 value = data[i+1:end]
>                 i = end + 1
>                 
>             sys.stdout.write('  =>  ' + type + ': ' + value + '\n')
>             
>     return server_response
> 
> def command_complete(conn, data):
>     sys.stdout.write('  => CommandComplete: ' + data + '\n')
> 
> def data_row(conn, data):
>     sys.stdout.write('  => DataRow ')
>     count, = struct.unpack('>H', data[:2])
>     o = 2
>     for i in xrange(count):
>         length, = struct.unpack('>i', data[o:o+4])
>         o += 4
>         if length == -1: sys.stdout.write('NULL,')
>         else:
>             sys.stdout.write(data[o:o+length] + ',')
>             o += length
>     sys.stdout.write('\n')
> 
> handlers = {
>     'K': make_log('BackendKeyData'),
>     '2': make_log('BindComplete'),
>     '3': make_log('CloseComplete'),
>     'C': command_complete,
>     'D': data_row,
>     'I': make_log('EmptyQuery'),
>     'E': make_server_response('ErrorResponse'),
>     'N': make_server_response('NoticeResponse'),
>     'S': make_log('ParameterStatus'),
>     '1': make_log('ParseComplete'),
>     'Z': make_log('ReadyForQuery')
>     }
>         
> def process_results(conn):
>     seen_sync = False
>     type = None
>     while type != 'Z':
>         type = conn.recv(1)
>         if not type:
>             raise RuntimeError('EOF seen')
> 
>         length, = struct.unpack('>i', conn.recv(4))
>         data = conn.recv(length-4)
> 
>         if not handlers.has_key(type):
>             raise RuntimeError('Unhandled message type ' + type)
>         
>         handlers[type](conn,data)
> 
> statement_number = 1
> def combos(conn, query, oids, params, params_2):
>     global statement_number
>     
>     send_parse(conn, '', query, oids)
>     send_parse(conn, '', query, oids)
>     send_bind(conn, '', '', params)
>     send_execute(conn, '', 0)
>     send_sync(conn)
>     process_results(conn)
> 
>     send_bind(conn, '', '', params_2)
>     send_execute(conn, '', 0)
>     send_sync(conn)
>     process_results(conn)
> 
>     stmt = 's_%d' % statement_number
>     statement_number += 1
>     send_parse(conn, stmt, query, oids)
>     send_bind(conn, '', stmt, params)
>     send_execute(conn, '', 0)
>     send_sync(conn)
>     process_results(conn)
> 
>     send_bind(conn, '', stmt, params_2)
>     send_execute(conn, '', 0)
>     send_sync(conn)
>     process_results(conn)
>     
> def tests(conn):
>     # Empty query
>     combos(conn=conn, query='', oids=(), params=(), params_2=())
>     
>     # Simple SELECT
>     combos(conn=conn, query='SELECT 1', oids=(), params=(), params_2=())
> 
>     # Simple parameterized SELECT
>     combos(conn=conn, query='SELECT $1', oids=(23,),
>            params=('42',), params_2=('24',))
> 
>     # Parameterized SELECT that calls a function that can be preevaluated
>     combos(conn=conn, query='SELECT abs($1)', oids=(23,),
>            params=('42',), params_2=('-24',))
> 
>     # Parameterized SELECT that calls a function that can't be preevaluated
>     combos(conn=conn, query='SELECT abs($1 + random())', oids=(23,),
>            params=('42',), params_2=('-24',))
> 
> if __name__ == '__main__':
>     host, port, db, user = sys.argv[1:]
>     conn = connect(host, int(port), db, user)
>     tests(conn)
>     send(conn, 'X', '')
>     conn.close()
>     

> 
> ---------------------------(end of broadcast)---------------------------
> TIP 5: Have you checked our extensive FAQ?
> 
>                http://www.postgresql.org/docs/faqs/FAQ.html

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  [EMAIL PROTECTED]               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
      subscribe-nomail command to [EMAIL PROTECTED] so that your
      message can get through to the mailing list cleanly

Reply via email to