]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/util/clauses.c
Update CVS HEAD for 2007 copyright. Back branches are typically not
[postgresql] / src / backend / optimizer / util / clauses.c
index c0ffd939cbe520f9a50e0f9b7c93c5b857df3a7b..d1ac0740c6400fd817ffbb00b03cef20b08d5632 100644 (file)
@@ -3,12 +3,12 @@
  * clauses.c
  *       routines to manipulate qualification clauses
  *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.136 2003/04/29 22:13:09 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.227 2007/01/05 22:19:32 momjian Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
 
 #include "postgres.h"
 
+#include "catalog/pg_aggregate.h"
 #include "catalog/pg_language.h"
+#include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "executor/executor.h"
+#include "executor/functions.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
 #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_coerce.h"
+#include "parser/parse_expr.h"
 #include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
-/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
-#define MAKEBOOLCONST(val,isnull) \
-       ((Node *) makeConst(BOOLOID, 1, (Datum) (val), (isnull), true))
+typedef struct
+{
+       List       *active_fns;
+       Node       *case_val;
+       bool            estimate;
+} eval_const_expressions_context;
 
 typedef struct
 {
@@ -51,24 +62,38 @@ typedef struct
 } substitute_actual_parameters_context;
 
 static bool contain_agg_clause_walker(Node *node, void *context);
-static bool contain_distinct_agg_clause_walker(Node *node, void *context);
-static bool count_agg_clause_walker(Node *node, int *count);
+static bool count_agg_clauses_walker(Node *node, AggClauseCounts *counts);
 static bool expression_returns_set_walker(Node *node, void *context);
 static bool contain_subplans_walker(Node *node, void *context);
 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 Node *eval_const_expressions_mutator(Node *node, List *active_fns);
+static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
+static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
+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,
+                                         eval_const_expressions_context *context,
+                                         bool *haveNull, bool *forceTrue);
+static List *simplify_and_arguments(List *args,
+                                          eval_const_expressions_context *context,
+                                          bool *haveNull, bool *forceFalse);
+static Expr *simplify_boolean_equality(List *args);
 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);
+                                 HeapTuple func_tuple,
+                                 eval_const_expressions_context *context);
 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);
+                                                        int *usecounts);
 static Node *substitute_actual_parameters_mutator(Node *node,
-                                        substitute_actual_parameters_context *context);
+                                                         substitute_actual_parameters_context *context);
+static void sql_inline_error_callback(void *arg);
 static Expr *evaluate_expr(Expr *expr, Oid result_type);
 
 
@@ -92,9 +117,9 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset,
        expr->opresulttype = opresulttype;
        expr->opretset = opretset;
        if (rightop)
-               expr->args = makeList2(leftop, rightop);
+               expr->args = list_make2(leftop, rightop);
        else
-               expr->args = makeList1(leftop);
+               expr->args = list_make1(leftop);
        return (Expr *) expr;
 }
 
@@ -107,10 +132,10 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset,
 Node *
 get_leftop(Expr *clause)
 {
-       OpExpr *expr = (OpExpr *) clause;
+       OpExpr     *expr = (OpExpr *) clause;
 
        if (expr->args != NIL)
-               return lfirst(expr->args);
+               return linitial(expr->args);
        else
                return NULL;
 }
@@ -124,36 +149,14 @@ get_leftop(Expr *clause)
 Node *
 get_rightop(Expr *clause)
 {
-       OpExpr *expr = (OpExpr *) clause;
+       OpExpr     *expr = (OpExpr *) clause;
 
-       if (expr->args != NIL && lnext(expr->args) != NIL)
-               return lfirst(lnext(expr->args));
+       if (list_length(expr->args) >= 2)
+               return lsecond(expr->args);
        else
                return NULL;
 }
 
-/*****************************************************************************
- *             FUNCTION clause functions
- *****************************************************************************/
-
-/*
- * make_funcclause
- *       Creates a function clause given its function info and argument list.
- */
-Expr *
-make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
-                               CoercionForm funcformat, List *funcargs)
-{
-       FuncExpr   *expr = makeNode(FuncExpr);
-
-       expr->funcid = funcid;
-       expr->funcresulttype = funcresulttype;
-       expr->funcretset = funcretset;
-       expr->funcformat = funcformat;
-       expr->args = funcargs;
-       return (Expr *) expr;
-}
-
 /*****************************************************************************
  *             NOT clause functions
  *****************************************************************************/
@@ -182,7 +185,7 @@ make_notclause(Expr *notclause)
        BoolExpr   *expr = makeNode(BoolExpr);
 
        expr->boolop = NOT_EXPR;
-       expr->args = makeList1(notclause);
+       expr->args = list_make1(notclause);
        return (Expr *) expr;
 }
 
@@ -194,7 +197,7 @@ make_notclause(Expr *notclause)
 Expr *
 get_notclausearg(Expr *notclause)
 {
-       return lfirst(((BoolExpr *) notclause)->args);
+       return linitial(((BoolExpr *) notclause)->args);
 }
 
 /*****************************************************************************
@@ -268,6 +271,9 @@ make_andclause(List *andclauses)
  * Variant of make_andclause for ANDing two qual conditions together.
  * Qual conditions have the property that a NULL nodetree is interpreted
  * as 'true'.
+ *
+ * NB: this makes no attempt to preserve AND/OR flatness; so it should not
+ * be used on a qual that has already been run through prepqual.c.
  */
 Node *
 make_and_qual(Node *qual1, Node *qual2)
@@ -276,12 +282,12 @@ make_and_qual(Node *qual1, Node *qual2)
                return qual2;
        if (qual2 == NULL)
                return qual1;
-       return (Node *) make_andclause(makeList2(qual1, qual2));
+       return (Node *) make_andclause(list_make2(qual1, qual2));
 }
 
 /*
- * Sometimes (such as in the result of canonicalize_qual or the input of
- * ExecQual), we use lists of expression nodes with implicit AND semantics.
+ * Sometimes (such as in the input of ExecQual), we use lists of expression
+ * nodes with implicit AND semantics.
  *
  * These functions convert between an AND-semantics expression list and the
  * ordinary representation of a boolean expression.
@@ -292,9 +298,9 @@ Expr *
 make_ands_explicit(List *andclauses)
 {
        if (andclauses == NIL)
-               return (Expr *) MAKEBOOLCONST(true, false);
-       else if (lnext(andclauses) == NIL)
-               return (Expr *) lfirst(andclauses);
+               return (Expr *) makeBoolConst(true, false);
+       else if (list_length(andclauses) == 1)
+               return (Expr *) linitial(andclauses);
        else
                return make_andclause(andclauses);
 }
@@ -303,10 +309,10 @@ List *
 make_ands_implicit(Expr *clause)
 {
        /*
-        * NB: because the parser sets the qual field to NULL in a query that
-        * has no WHERE clause, we must consider a NULL input clause as TRUE,
-        * even though one might more reasonably think it FALSE.  Grumble. If
-        * this causes trouble, consider changing the parser's behavior.
+        * NB: because the parser sets the qual field to NULL in a query that has
+        * no WHERE clause, we must consider a NULL input clause as TRUE, even
+        * though one might more reasonably think it FALSE.  Grumble. If this
+        * causes trouble, consider changing the parser's behavior.
         */
        if (clause == NULL)
                return NIL;                             /* NULL -> NIL list == TRUE */
@@ -317,7 +323,7 @@ make_ands_implicit(Expr *clause)
                         DatumGetBool(((Const *) clause)->constvalue))
                return NIL;                             /* constant TRUE input -> NIL list */
        else
-               return makeList1(clause);
+               return list_make1(clause);
 }
 
 
@@ -330,6 +336,13 @@ make_ands_implicit(Expr *clause)
  *       Recursively search for Aggref nodes within a clause.
  *
  *       Returns true if any aggregate found.
+ *
+ * This does not descend into subqueries, and so should be used only after
+ * reduction of sublinks to subplans, or in contexts where it's known there
+ * are no subqueries.  There mustn't be outer-aggregate references either.
+ *
+ * (If you want something like this but able to deal with subqueries,
+ * see rewriteManip.c's checkExprHasAggs().)
  */
 bool
 contain_agg_clause(Node *clause)
@@ -339,79 +352,145 @@ contain_agg_clause(Node *clause)
 
 static bool
 contain_agg_clause_walker(Node *node, void *context)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Aggref))
-               return true;                    /* abort the tree traversal and return
-                                                                * true */
-       return expression_tree_walker(node, contain_agg_clause_walker, context);
-}
-
-/*
- * contain_distinct_agg_clause
- *       Recursively search for DISTINCT Aggref nodes within a clause.
- *
- *       Returns true if any DISTINCT aggregate found.
- */
-bool
-contain_distinct_agg_clause(Node *clause)
-{
-       return contain_distinct_agg_clause_walker(clause, NULL);
-}
-
-static bool
-contain_distinct_agg_clause_walker(Node *node, void *context)
 {
        if (node == NULL)
                return false;
        if (IsA(node, Aggref))
        {
-               if (((Aggref *) node)->aggdistinct)
-                       return true;            /* abort the tree traversal and return
-                                                                * true */
+               Assert(((Aggref *) node)->agglevelsup == 0);
+               return true;                    /* abort the tree traversal and return true */
        }
-       return expression_tree_walker(node, contain_distinct_agg_clause_walker, context);
+       Assert(!IsA(node, SubLink));
+       return expression_tree_walker(node, contain_agg_clause_walker, context);
 }
 
 /*
- * count_agg_clause
+ * count_agg_clauses
  *       Recursively count the Aggref nodes in an expression tree.
  *
  *       Note: this also checks for nested aggregates, which are an error.
+ *
+ * We not only count the nodes, but attempt to estimate the total space
+ * needed for their transition state values if all are evaluated in parallel
+ * (as would be done in a HashAgg plan).  See AggClauseCounts for the exact
+ * set of statistics returned.
+ *
+ * NOTE that the counts are ADDED to those already in *counts ... so the
+ * caller is responsible for zeroing the struct initially.
+ *
+ * This does not descend into subqueries, and so should be used only after
+ * reduction of sublinks to subplans, or in contexts where it's known there
+ * are no subqueries.  There mustn't be outer-aggregate references either.
  */
-int
-count_agg_clause(Node *clause)
+void
+count_agg_clauses(Node *clause, AggClauseCounts *counts)
 {
-       int                     result = 0;
-
-       count_agg_clause_walker(clause, &result);
-       return result;
+       /* no setup needed */
+       count_agg_clauses_walker(clause, counts);
 }
 
 static bool
-count_agg_clause_walker(Node *node, int *count)
+count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
 {
        if (node == NULL)
                return false;
        if (IsA(node, Aggref))
        {
-               (*count)++;
+               Aggref     *aggref = (Aggref *) node;
+               Oid                *inputTypes;
+               int                     numArguments;
+               HeapTuple       aggTuple;
+               Form_pg_aggregate aggform;
+               Oid                     aggtranstype;
+               int                     i;
+               ListCell   *l;
+
+               Assert(aggref->agglevelsup == 0);
+               counts->numAggs++;
+               if (aggref->aggdistinct)
+                       counts->numDistinctAggs++;
+
+               /* extract argument types */
+               numArguments = list_length(aggref->args);
+               inputTypes = (Oid *) palloc(sizeof(Oid) * numArguments);
+               i = 0;
+               foreach(l, aggref->args)
+               {
+                       inputTypes[i++] = exprType((Node *) lfirst(l));
+               }
+
+               /* fetch aggregate transition datatype from pg_aggregate */
+               aggTuple = SearchSysCache(AGGFNOID,
+                                                                 ObjectIdGetDatum(aggref->aggfnoid),
+                                                                 0, 0, 0);
+               if (!HeapTupleIsValid(aggTuple))
+                       elog(ERROR, "cache lookup failed for aggregate %u",
+                                aggref->aggfnoid);
+               aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+               aggtranstype = aggform->aggtranstype;
+               ReleaseSysCache(aggTuple);
+
+               /* resolve actual type of transition state, if polymorphic */
+               if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
+               {
+                       /* have to fetch the agg's declared input types... */
+                       Oid                *declaredArgTypes;
+                       int                     agg_nargs;
+
+                       (void) get_func_signature(aggref->aggfnoid,
+                                                                         &declaredArgTypes, &agg_nargs);
+                       Assert(agg_nargs == numArguments);
+                       aggtranstype = enforce_generic_type_consistency(inputTypes,
+                                                                                                                       declaredArgTypes,
+                                                                                                                       agg_nargs,
+                                                                                                                       aggtranstype);
+                       pfree(declaredArgTypes);
+               }
+
+               /*
+                * If the transition type is pass-by-value then it doesn't add
+                * anything to the required size of the hashtable.      If it is
+                * pass-by-reference then we have to add the estimated size of the
+                * value itself, plus palloc overhead.
+                */
+               if (!get_typbyval(aggtranstype))
+               {
+                       int32           aggtranstypmod;
+                       int32           avgwidth;
+
+                       /*
+                        * If transition state is of same type as first input, assume it's
+                        * the same typmod (same width) as well.  This works for cases
+                        * like MAX/MIN and is probably somewhat reasonable otherwise.
+                        */
+                       if (numArguments > 0 && aggtranstype == inputTypes[0])
+                               aggtranstypmod = exprTypmod((Node *) linitial(aggref->args));
+                       else
+                               aggtranstypmod = -1;
+
+                       avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod);
+                       avgwidth = MAXALIGN(avgwidth);
+
+                       counts->transitionSpace += avgwidth + 2 * sizeof(void *);
+               }
 
                /*
-                * Complain if the aggregate's argument contains any aggregates;
+                * Complain if the aggregate's arguments contain any aggregates;
                 * nested agg functions are semantically nonsensical.
                 */
-               if (contain_agg_clause((Node *) ((Aggref *) node)->target))
-                       elog(ERROR, "Aggregate function calls may not be nested");
+               if (contain_agg_clause((Node *) aggref->args))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_GROUPING_ERROR),
+                                        errmsg("aggregate function calls may not be nested")));
 
                /*
                 * Having checked that, we need not recurse into the argument.
                 */
                return false;
        }
-       return expression_tree_walker(node, count_agg_clause_walker,
-                                                                 (void *) count);
+       Assert(!IsA(node, SubLink));
+       return expression_tree_walker(node, count_agg_clauses_walker,
+                                                                 (void *) counts);
 }
 
 
@@ -448,7 +527,7 @@ expression_returns_set_walker(Node *node, void *context)
        }
        if (IsA(node, OpExpr))
        {
-               OpExpr   *expr = (OpExpr *) node;
+               OpExpr     *expr = (OpExpr *) node;
 
                if (expr->opretset)
                        return true;
@@ -460,6 +539,8 @@ expression_returns_set_walker(Node *node, void *context)
                return false;
        if (IsA(node, DistinctExpr))
                return false;
+       if (IsA(node, ScalarArrayOpExpr))
+               return false;
        if (IsA(node, BoolExpr))
                return false;
        if (IsA(node, SubLink))
@@ -468,8 +549,16 @@ expression_returns_set_walker(Node *node, void *context)
                return false;
        if (IsA(node, ArrayExpr))
                return false;
+       if (IsA(node, RowExpr))
+               return false;
+       if (IsA(node, RowCompareExpr))
+               return false;
        if (IsA(node, CoalesceExpr))
                return false;
+       if (IsA(node, MinMaxExpr))
+               return false;
+       if (IsA(node, XmlExpr))
+               return false;
        if (IsA(node, NullIfExpr))
                return false;
 
@@ -505,8 +594,7 @@ contain_subplans_walker(Node *node, void *context)
                return false;
        if (IsA(node, SubPlan) ||
                IsA(node, SubLink))
-               return true;                    /* abort the tree traversal and return
-                                                                * true */
+               return true;                    /* abort the tree traversal and return true */
        return expression_tree_walker(node, contain_subplans_walker, context);
 }
 
@@ -548,7 +636,7 @@ contain_mutable_functions_walker(Node *node, void *context)
        }
        if (IsA(node, OpExpr))
        {
-               OpExpr   *expr = (OpExpr *) node;
+               OpExpr     *expr = (OpExpr *) node;
 
                if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
                        return true;
@@ -556,7 +644,15 @@ contain_mutable_functions_walker(Node *node, void *context)
        }
        if (IsA(node, DistinctExpr))
        {
-               DistinctExpr   *expr = (DistinctExpr *) node;
+               DistinctExpr *expr = (DistinctExpr *) node;
+
+               if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
+                       return true;
+               /* else fall through to check args */
+       }
+       if (IsA(node, ScalarArrayOpExpr))
+       {
+               ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
 
                if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
                        return true;
@@ -564,20 +660,20 @@ contain_mutable_functions_walker(Node *node, void *context)
        }
        if (IsA(node, NullIfExpr))
        {
-               NullIfExpr   *expr = (NullIfExpr *) node;
+               NullIfExpr *expr = (NullIfExpr *) node;
 
                if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, RowCompareExpr))
        {
-               SubLink    *sublink = (SubLink *) node;
-               List       *opid;
+               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+               ListCell   *opid;
 
-               foreach(opid, sublink->operOids)
+               foreach(opid, rcexpr->opnos)
                {
-                       if (op_volatile(lfirsto(opid)) != PROVOLATILE_IMMUTABLE)
+                       if (op_volatile(lfirst_oid(opid)) != PROVOLATILE_IMMUTABLE)
                                return true;
                }
                /* else fall through to check args */
@@ -623,7 +719,7 @@ contain_volatile_functions_walker(Node *node, void *context)
        }
        if (IsA(node, OpExpr))
        {
-               OpExpr   *expr = (OpExpr *) node;
+               OpExpr     *expr = (OpExpr *) node;
 
                if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
                        return true;
@@ -631,7 +727,15 @@ contain_volatile_functions_walker(Node *node, void *context)
        }
        if (IsA(node, DistinctExpr))
        {
-               DistinctExpr   *expr = (DistinctExpr *) node;
+               DistinctExpr *expr = (DistinctExpr *) node;
+
+               if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
+                       return true;
+               /* else fall through to check args */
+       }
+       if (IsA(node, ScalarArrayOpExpr))
+       {
+               ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
 
                if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
                        return true;
@@ -639,20 +743,21 @@ contain_volatile_functions_walker(Node *node, void *context)
        }
        if (IsA(node, NullIfExpr))
        {
-               NullIfExpr   *expr = (NullIfExpr *) node;
+               NullIfExpr *expr = (NullIfExpr *) node;
 
                if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, RowCompareExpr))
        {
-               SubLink    *sublink = (SubLink *) node;
-               List       *opid;
+               /* RowCompare probably can't have volatile ops, but check anyway */
+               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+               ListCell   *opid;
 
-               foreach(opid, sublink->operOids)
+               foreach(opid, rcexpr->opnos)
                {
-                       if (op_volatile(lfirsto(opid)) == PROVOLATILE_VOLATILE)
+                       if (op_volatile(lfirst_oid(opid)) == PROVOLATILE_VOLATILE)
                                return true;
                }
                /* else fall through to check args */
@@ -673,10 +778,10 @@ contain_volatile_functions_walker(Node *node, void *context)
  * Returns true if any nonstrict construct is found --- ie, anything that
  * could produce non-NULL output with a NULL input.
  *
- * XXX we do not examine sub-selects to see if they contain uses of
- * nonstrict functions.        It's not real clear if that is correct or not...
- * for the current usage it does not matter, since inline_function()
- * rejects cases with sublinks.
+ * The idea here is that the caller has verified that the expression contains
+ * one or more Var or Param nodes (as appropriate for the caller's need), and
+ * now wishes to prove that the expression result will be NULL if any of these
+ * inputs is NULL.     If we return false, then the proof succeeded.
  */
 bool
 contain_nonstrict_functions(Node *clause)
@@ -689,6 +794,18 @@ contain_nonstrict_functions_walker(Node *node, void *context)
 {
        if (node == NULL)
                return false;
+       if (IsA(node, Aggref))
+       {
+               /* an aggregate could return non-null with null input */
+               return true;
+       }
+       if (IsA(node, ArrayRef))
+       {
+               /* array assignment is nonstrict, but subscripting is strict */
+               if (((ArrayRef *) node)->refassgnexpr != NULL)
+                       return true;
+               /* else fall through to check args */
+       }
        if (IsA(node, FuncExpr))
        {
                FuncExpr   *expr = (FuncExpr *) node;
@@ -699,7 +816,7 @@ contain_nonstrict_functions_walker(Node *node, void *context)
        }
        if (IsA(node, OpExpr))
        {
-               OpExpr   *expr = (OpExpr *) node;
+               OpExpr     *expr = (OpExpr *) node;
 
                if (!op_strict(expr->opno))
                        return true;
@@ -710,45 +827,236 @@ contain_nonstrict_functions_walker(Node *node, void *context)
                /* IS DISTINCT FROM is inherently non-strict */
                return true;
        }
+       if (IsA(node, ScalarArrayOpExpr))
+       {
+               ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+               if (!is_strict_saop(expr, false))
+                       return true;
+               /* else fall through to check args */
+       }
        if (IsA(node, BoolExpr))
        {
                BoolExpr   *expr = (BoolExpr *) node;
 
                switch (expr->boolop)
                {
-                       case OR_EXPR:
                        case AND_EXPR:
-                               /* OR, AND are inherently non-strict */
+                       case OR_EXPR:
+                               /* AND, OR are inherently non-strict */
                                return true;
                        default:
                                break;
                }
        }
+       if (IsA(node, SubLink))
+       {
+               /* In some cases a sublink might be strict, but in general not */
+               return true;
+       }
+       if (IsA(node, SubPlan))
+               return true;
+       if (IsA(node, FieldStore))
+               return true;
        if (IsA(node, CaseExpr))
                return true;
-       /* NB: ArrayExpr might someday be nonstrict */
+       if (IsA(node, CaseWhen))
+               return true;
+       if (IsA(node, ArrayExpr))
+               return true;
+       if (IsA(node, RowExpr))
+               return true;
+       if (IsA(node, RowCompareExpr))
+               return true;
        if (IsA(node, CoalesceExpr))
                return true;
+       if (IsA(node, MinMaxExpr))
+               return true;
+       if (IsA(node, XmlExpr))
+               return true;
        if (IsA(node, NullIfExpr))
                return true;
        if (IsA(node, NullTest))
                return true;
        if (IsA(node, BooleanTest))
                return true;
-       if (IsA(node, SubLink))
+       return expression_tree_walker(node, contain_nonstrict_functions_walker,
+                                                                 context);
+}
+
+
+/*
+ * find_nonnullable_rels
+ *             Determine which base rels are forced nonnullable by given clause.
+ *
+ * Returns the set of all Relids that are referenced in the clause in such
+ * a way that the clause cannot possibly return TRUE if any of these Relids
+ * is an all-NULL row. (It is OK to err on the side of conservatism; hence
+ * the analysis here is simplistic.)
+ *
+ * The semantics here are subtly different from contain_nonstrict_functions:
+ * that function is concerned with NULL results from arbitrary expressions,
+ * but here we assume that the input is a Boolean expression, and wish to
+ * see if NULL inputs will provably cause a FALSE-or-NULL result.  We expect
+ * the expression to have been AND/OR flattened and converted to implicit-AND
+ * format.
+ *
+ * We don't use expression_tree_walker here because we don't want to
+ * descend through very many kinds of nodes; only the ones we can be sure
+ * are strict. We can descend through the top level of implicit AND'ing,
+ * but not through any explicit ANDs (or ORs) below that, since those are not
+ * strict constructs.  The List case handles the top-level implicit AND list
+ * as well as lists of arguments to strict operators/functions.
+ */
+Relids
+find_nonnullable_rels(Node *clause)
+{
+       return find_nonnullable_rels_walker(clause, true);
+}
+
+static Relids
+find_nonnullable_rels_walker(Node *node, bool top_level)
+{
+       Relids          result = NULL;
+
+       if (node == NULL)
+               return NULL;
+       if (IsA(node, Var))
        {
-               SubLink    *sublink = (SubLink *) node;
-               List       *opid;
+               Var                *var = (Var *) node;
 
-               foreach(opid, sublink->operOids)
+               if (var->varlevelsup == 0)
+                       result = bms_make_singleton(var->varno);
+       }
+       else if (IsA(node, List))
+       {
+               ListCell   *l;
+
+               foreach(l, (List *) node)
                {
-                       if (!op_strict(lfirsto(opid)))
-                               return true;
+                       result = bms_join(result,
+                                                         find_nonnullable_rels_walker(lfirst(l),
+                                                                                                                  top_level));
                }
-               /* else fall through to check args */
        }
-       return expression_tree_walker(node, contain_nonstrict_functions_walker,
-                                                                 context);
+       else if (IsA(node, FuncExpr))
+       {
+               FuncExpr   *expr = (FuncExpr *) node;
+
+               if (func_strict(expr->funcid))
+                       result = find_nonnullable_rels_walker((Node *) expr->args, false);
+       }
+       else if (IsA(node, OpExpr))
+       {
+               OpExpr     *expr = (OpExpr *) node;
+
+               if (op_strict(expr->opno))
+                       result = find_nonnullable_rels_walker((Node *) expr->args, false);
+       }
+       else if (IsA(node, ScalarArrayOpExpr))
+       {
+               ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+               if (is_strict_saop(expr, true))
+                       result = find_nonnullable_rels_walker((Node *) expr->args, false);
+       }
+       else if (IsA(node, BoolExpr))
+       {
+               BoolExpr   *expr = (BoolExpr *) node;
+
+               /* NOT is strict, others are not */
+               if (expr->boolop == NOT_EXPR)
+                       result = find_nonnullable_rels_walker((Node *) expr->args, false);
+       }
+       else if (IsA(node, RelabelType))
+       {
+               RelabelType *expr = (RelabelType *) node;
+
+               result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
+       }
+       else if (IsA(node, ConvertRowtypeExpr))
+       {
+               /* not clear this is useful, but it can't hurt */
+               ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node;
+
+               result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
+       }
+       else if (IsA(node, NullTest))
+       {
+               NullTest   *expr = (NullTest *) node;
+
+               /*
+                * IS NOT NULL can be considered strict, but only at top level; else
+                * we might have something like NOT (x IS NOT NULL).
+                */
+               if (top_level && expr->nulltesttype == IS_NOT_NULL)
+                       result = find_nonnullable_rels_walker((Node *) expr->arg, false);
+       }
+       else if (IsA(node, BooleanTest))
+       {
+               BooleanTest *expr = (BooleanTest *) node;
+
+               /*
+                * Appropriate boolean tests are strict at top level.
+                */
+               if (top_level &&
+                       (expr->booltesttype == IS_TRUE ||
+                        expr->booltesttype == IS_FALSE ||
+                        expr->booltesttype == IS_NOT_UNKNOWN))
+                       result = find_nonnullable_rels_walker((Node *) expr->arg, false);
+       }
+       return result;
+}
+
+/*
+ * Can we treat a ScalarArrayOpExpr as strict?
+ *
+ * If "falseOK" is true, then a "false" result can be considered strict,
+ * else we need to guarantee an actual NULL result for NULL input.
+ *
+ * "foo op ALL array" is strict if the op is strict *and* we can prove
+ * that the array input isn't an empty array.  We can check that
+ * for the cases of an array constant and an ARRAY[] construct.
+ *
+ * "foo op ANY array" is strict in the falseOK sense if the op is strict.
+ * If not falseOK, the test is the same as for "foo op ALL array".
+ */
+static bool
+is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK)
+{
+       Node       *rightop;
+
+       /* The contained operator must be strict. */
+       if (!op_strict(expr->opno))
+               return false;
+       /* If ANY and falseOK, that's all we need to check. */
+       if (expr->useOr && falseOK)
+               return true;
+       /* Else, we have to see if the array is provably non-empty. */
+       Assert(list_length(expr->args) == 2);
+       rightop = (Node *) lsecond(expr->args);
+       if (rightop && IsA(rightop, Const))
+       {
+               Datum           arraydatum = ((Const *) rightop)->constvalue;
+               bool            arrayisnull = ((Const *) rightop)->constisnull;
+               ArrayType  *arrayval;
+               int                     nitems;
+
+               if (arrayisnull)
+                       return false;
+               arrayval = DatumGetArrayTypeP(arraydatum);
+               nitems = ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval));
+               if (nitems > 0)
+                       return true;
+       }
+       else if (rightop && IsA(rightop, ArrayExpr))
+       {
+               ArrayExpr  *arrayexpr = (ArrayExpr *) rightop;
+
+               if (arrayexpr->elements != NIL && !arrayexpr->multidims)
+                       return true;
+       }
+       return false;
 }
 
 
@@ -758,23 +1066,22 @@ contain_nonstrict_functions_walker(Node *node, void *context)
 
 /*
  * is_pseudo_constant_clause
- *       Detect whether a clause is "constant", ie, it contains no variables
- *       of the current query level and no uses of volatile functions.
- *       Such a clause is not necessarily a true constant: it can still contain
+ *       Detect whether an expression is "pseudo constant", ie, it contains no
+ *       variables of the current query level and no uses of volatile functions.
+ *       Such an expr is not necessarily a true constant: it can still contain
  *       Params and outer-level Vars, not to mention functions whose results
- *       may vary from one statement to the next.      However, the clause's value
+ *       may vary from one statement to the next.      However, the expr's value
  *       will be constant over any one scan of the current query, so it can be
- *       used as an indexscan key or (if a top-level qual) can be pushed up to
- *       become a gating qual.
+ *       used as, eg, an indexscan key.
  */
 bool
 is_pseudo_constant_clause(Node *clause)
 {
        /*
         * We could implement this check in one recursive scan.  But since the
-        * check for volatile functions is both moderately expensive and
-        * unlikely to fail, it seems better to look for Vars first and only
-        * check for volatile functions if we find no Vars.
+        * check for volatile functions is both moderately expensive and unlikely
+        * to fail, it seems better to look for Vars first and only check for
+        * volatile functions if we find no Vars.
         */
        if (!contain_var_clause(clause) &&
                !contain_volatile_functions(clause))
@@ -783,31 +1090,17 @@ is_pseudo_constant_clause(Node *clause)
 }
 
 /*
- * pull_constant_clauses
- *             Scan through a list of qualifications and separate "constant" quals
- *             from those that are not.
- *
- * Returns a list of the pseudo-constant clauses in constantQual and the
- * remaining quals as the return value.
+ * is_pseudo_constant_clause_relids
+ *       Same as above, except caller already has available the var membership
+ *       of the expression; this lets us avoid the contain_var_clause() scan.
  */
-List *
-pull_constant_clauses(List *quals, List **constantQual)
+bool
+is_pseudo_constant_clause_relids(Node *clause, Relids relids)
 {
-       List       *constqual = NIL;
-       List       *restqual = NIL;
-       List       *q;
-
-       foreach(q, quals)
-       {
-               Node       *qual = (Node *) lfirst(q);
-
-               if (is_pseudo_constant_clause(qual))
-                       constqual = lappend(constqual, qual);
-               else
-                       restqual = lappend(restqual, qual);
-       }
-       *constantQual = constqual;
-       return restqual;
+       if (bms_is_empty(relids) &&
+               !contain_volatile_functions(clause))
+               return true;
+       return false;
 }
 
 
@@ -826,7 +1119,7 @@ pull_constant_clauses(List *quals, List **constantQual)
 bool
 has_distinct_on_clause(Query *query)
 {
-       List       *targetList;
+       ListCell   *l;
 
        /* Is there a DISTINCT clause at all? */
        if (query->distinctClause == NIL)
@@ -834,37 +1127,36 @@ has_distinct_on_clause(Query *query)
 
        /*
         * If the DISTINCT list contains all the nonjunk targetlist items, and
-        * nothing else (ie, no junk tlist items), then it's a simple
-        * DISTINCT, else it's DISTINCT ON.  We do not require the lists to be
-        * in the same order (since the parser may have adjusted the DISTINCT
-        * clause ordering to agree with ORDER BY).  Furthermore, a
-        * non-DISTINCT junk tlist item that is in the sortClause is also
-        * evidence of DISTINCT ON, since we don't allow ORDER BY on junk
-        * tlist items when plain DISTINCT is used.
+        * nothing else (ie, no junk tlist items), then it's a simple DISTINCT,
+        * else it's DISTINCT ON.  We do not require the lists to be in the same
+        * order (since the parser may have adjusted the DISTINCT clause ordering
+        * to agree with ORDER BY).  Furthermore, a non-DISTINCT junk tlist item
+        * that is in the sortClause is also evidence of DISTINCT ON, since we
+        * don't allow ORDER BY on junk tlist items when plain DISTINCT is used.
         *
         * This code assumes that the DISTINCT list is valid, ie, all its entries
         * match some entry of the tlist.
         */
-       foreach(targetList, query->targetList)
+       foreach(l, query->targetList)
        {
-               TargetEntry *tle = (TargetEntry *) lfirst(targetList);
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
 
-               if (tle->resdom->ressortgroupref == 0)
+               if (tle->ressortgroupref == 0)
                {
-                       if (tle->resdom->resjunk)
+                       if (tle->resjunk)
                                continue;               /* we can ignore unsorted junk cols */
                        return true;            /* definitely not in DISTINCT list */
                }
                if (targetIsInSortList(tle, query->distinctClause))
                {
-                       if (tle->resdom->resjunk)
+                       if (tle->resjunk)
                                return true;    /* junk TLE in DISTINCT means DISTINCT ON */
                        /* else this TLE is okay, keep looking */
                }
                else
                {
                        /* This TLE is not in DISTINCT list */
-                       if (!tle->resdom->resjunk)
+                       if (!tle->resjunk)
                                return true;    /* non-junk, non-DISTINCT, so DISTINCT ON */
                        if (targetIsInSortList(tle, query->sortClause))
                                return true;    /* sorted, non-distinct junk */
@@ -875,57 +1167,27 @@ has_distinct_on_clause(Query *query)
        return false;
 }
 
-
-/*****************************************************************************
- *                                                                                                                                                      *
- *             General clause-manipulating routines                                                             *
- *                                                                                                                                                      *
- *****************************************************************************/
-
 /*
- * clause_get_relids_vars
- *       Retrieves distinct relids and vars appearing within a clause.
- *
- * '*relids' is set to the set of all distinct "varno"s appearing
- *             in Vars within the clause.
- * '*vars' is set to a list of all distinct Vars appearing within the clause.
- *             Var nodes are considered distinct if they have different varno
- *             or varattno values.  If there are several occurrences of the same
- *             varno/varattno, you get a randomly chosen one...
- *
- * Note that upper-level vars are ignored, since they normally will
- * become Params with respect to this query level.
+ * Test whether a query uses simple DISTINCT, ie, has a distinct-list that
+ * is the same as the set of output columns.
  */
-void
-clause_get_relids_vars(Node *clause, Relids *relids, List **vars)
+bool
+has_distinct_clause(Query *query)
 {
-       List       *clvars = pull_var_clause(clause, false);
-       Relids          varnos = NULL;
-       List       *var_list = NIL;
-       List       *i;
-
-       foreach(i, clvars)
-       {
-               Var                *var = (Var *) lfirst(i);
-               List       *vi;
+       /* Is there a DISTINCT clause at all? */
+       if (query->distinctClause == NIL)
+               return false;
 
-               varnos = bms_add_member(varnos, var->varno);
-               foreach(vi, var_list)
-               {
-                       Var                *in_list = (Var *) lfirst(vi);
+       /* It's DISTINCT if it's not DISTINCT ON */
+       return !has_distinct_on_clause(query);
+}
 
-                       if (in_list->varno == var->varno &&
-                               in_list->varattno == var->varattno)
-                               break;
-               }
-               if (vi == NIL)
-                       var_list = lcons(var, var_list);
-       }
-       freeList(clvars);
 
-       *relids = varnos;
-       *vars = var_list;
-}
+/*****************************************************************************
+ *                                                                                                                                                      *
+ *             General clause-manipulating routines                                                             *
+ *                                                                                                                                                      *
+ *****************************************************************************/
 
 /*
  * NumRelids
@@ -944,24 +1206,25 @@ NumRelids(Node *clause)
 }
 
 /*
- * CommuteClause: commute a binary operator clause
+ * CommuteOpExpr: commute a binary operator clause
  *
  * XXX the clause is destructively modified!
  */
 void
-CommuteClause(OpExpr *clause)
+CommuteOpExpr(OpExpr *clause)
 {
        Oid                     opoid;
        Node       *temp;
 
+       /* Sanity checks: caller is at fault if these fail */
        if (!is_opclause(clause) ||
-               length(clause->args) != 2)
-               elog(ERROR, "CommuteClause: applied to non-binary-operator clause");
+               list_length(clause->args) != 2)
+               elog(ERROR, "cannot commute non-binary-operator clause");
 
        opoid = get_commutator(clause->opno);
 
        if (!OidIsValid(opoid))
-               elog(ERROR, "CommuteClause: no commutator for operator %u",
+               elog(ERROR, "could not find commutator for operator %u",
                         clause->opno);
 
        /*
@@ -971,49 +1234,302 @@ CommuteClause(OpExpr *clause)
        clause->opfuncid = InvalidOid;
        /* opresulttype and opretset are assumed not to change */
 
-       temp = lfirst(clause->args);
-       lfirst(clause->args) = lsecond(clause->args);
+       temp = linitial(clause->args);
+       linitial(clause->args) = lsecond(clause->args);
        lsecond(clause->args) = temp;
 }
 
-
-/*--------------------
- * eval_const_expressions
- *
- * Reduce any recognizably constant subexpressions of the given
- * expression tree, for example "2 + 2" => "4".  More interestingly,
- * we can reduce certain boolean expressions even when they contain
- * non-constant subexpressions: "x OR true" => "true" no matter what
- * the subexpression x is.     (XXX We assume that no such subexpression
- * will have important side-effects, which is not necessarily a good
- * assumption in the presence of user-defined functions; do we need a
- * pg_proc flag that prevents discarding the execution of a function?)
- *
- * We do understand that certain functions may deliver non-constant
- * results even with constant inputs, "nextval()" being the classic
- * example.  Functions that are not marked "immutable" in pg_proc
- * will not be pre-evaluated here, although we will reduce their
- * arguments as far as possible.
+/*
+ * CommuteRowCompareExpr: commute a RowCompareExpr clause
  *
- * We assume that the tree has already been type-checked and contains
- * only operators and functions that are reasonable to try to execute.
- *--------------------
+ * XXX the clause is destructively modified!
  */
-Node *
-eval_const_expressions(Node *node)
+void
+CommuteRowCompareExpr(RowCompareExpr *clause)
 {
-       /*
-        * 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);
-}
+       List       *newops;
+       List       *temp;
+       ListCell   *l;
 
-static Node *
-eval_const_expressions_mutator(Node *node, List *active_fns)
-{
-       if (node == NULL)
+       /* Sanity checks: caller is at fault if these fail */
+       if (!IsA(clause, RowCompareExpr))
+               elog(ERROR, "expected a RowCompareExpr");
+
+       /* Build list of commuted operators */
+       newops = NIL;
+       foreach(l, clause->opnos)
+       {
+               Oid                     opoid = lfirst_oid(l);
+
+               opoid = get_commutator(opoid);
+               if (!OidIsValid(opoid))
+                       elog(ERROR, "could not find commutator for operator %u",
+                                lfirst_oid(l));
+               newops = lappend_oid(newops, opoid);
+       }
+
+       /*
+        * modify the clause in-place!
+        */
+       switch (clause->rctype)
+       {
+               case ROWCOMPARE_LT:
+                       clause->rctype = ROWCOMPARE_GT;
+                       break;
+               case ROWCOMPARE_LE:
+                       clause->rctype = ROWCOMPARE_GE;
+                       break;
+               case ROWCOMPARE_GE:
+                       clause->rctype = ROWCOMPARE_LE;
+                       break;
+               case ROWCOMPARE_GT:
+                       clause->rctype = ROWCOMPARE_LT;
+                       break;
+               default:
+                       elog(ERROR, "unexpected RowCompare type: %d",
+                                (int) clause->rctype);
+                       break;
+       }
+
+       clause->opnos = newops;
+
+       /*
+        * Note: we need not change the opfamilies list; we assume any btree
+        * opfamily containing an operator will also contain its commutator.
+        */
+
+       temp = clause->largs;
+       clause->largs = clause->rargs;
+       clause->rargs = temp;
+}
+
+/*
+ * strip_implicit_coercions: remove implicit coercions at top level of tree
+ *
+ * Note: there isn't any useful thing we can do with a RowExpr here, so
+ * just return it unchanged, even if it's marked as an implicit coercion.
+ */
+Node *
+strip_implicit_coercions(Node *node)
+{
+       if (node == NULL)
+               return NULL;
+       if (IsA(node, FuncExpr))
+       {
+               FuncExpr   *f = (FuncExpr *) node;
+
+               if (f->funcformat == COERCE_IMPLICIT_CAST)
+                       return strip_implicit_coercions(linitial(f->args));
+       }
+       else if (IsA(node, RelabelType))
+       {
+               RelabelType *r = (RelabelType *) node;
+
+               if (r->relabelformat == COERCE_IMPLICIT_CAST)
+                       return strip_implicit_coercions((Node *) r->arg);
+       }
+       else if (IsA(node, ConvertRowtypeExpr))
+       {
+               ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node;
+
+               if (c->convertformat == COERCE_IMPLICIT_CAST)
+                       return strip_implicit_coercions((Node *) c->arg);
+       }
+       else if (IsA(node, CoerceToDomain))
+       {
+               CoerceToDomain *c = (CoerceToDomain *) node;
+
+               if (c->coercionformat == COERCE_IMPLICIT_CAST)
+                       return strip_implicit_coercions((Node *) c->arg);
+       }
+       return node;
+}
+
+/*
+ * set_coercionform_dontcare: set all CoercionForm fields to COERCE_DONTCARE
+ *
+ * This is used to make index expressions and index predicates more easily
+ * comparable to clauses of queries.  CoercionForm is not semantically
+ * significant (for cases where it does matter, the significant info is
+ * coded into the coercion function arguments) so we can ignore it during
+ * comparisons.  Thus, for example, an index on "foo::int4" can match an
+ * implicit coercion to int4.
+ *
+ * Caution: the passed expression tree is modified in-place.
+ */
+void
+set_coercionform_dontcare(Node *node)
+{
+       (void) set_coercionform_dontcare_walker(node, NULL);
+}
+
+static bool
+set_coercionform_dontcare_walker(Node *node, void *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, FuncExpr))
+               ((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
+       else if (IsA(node, RelabelType))
+               ((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
+       else if (IsA(node, ConvertRowtypeExpr))
+               ((ConvertRowtypeExpr *) node)->convertformat = COERCE_DONTCARE;
+       else if (IsA(node, RowExpr))
+               ((RowExpr *) node)->row_format = COERCE_DONTCARE;
+       else if (IsA(node, CoerceToDomain))
+               ((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE;
+       return expression_tree_walker(node, set_coercionform_dontcare_walker,
+                                                                 context);
+}
+
+/*
+ * Helper for eval_const_expressions: check that datatype of an attribute
+ * is still what it was when the expression was parsed.  This is needed to
+ * guard against improper simplification after ALTER COLUMN TYPE.  (XXX we
+ * may well need to make similar checks elsewhere?)
+ */
+static bool
+rowtype_field_matches(Oid rowtypeid, int fieldnum,
+                                         Oid expectedtype, int32 expectedtypmod)
+{
+       TupleDesc       tupdesc;
+       Form_pg_attribute attr;
+
+       /* No issue for RECORD, since there is no way to ALTER such a type */
+       if (rowtypeid == RECORDOID)
+               return true;
+       tupdesc = lookup_rowtype_tupdesc(rowtypeid, -1);
+       if (fieldnum <= 0 || fieldnum > tupdesc->natts)
+       {
+               ReleaseTupleDesc(tupdesc);
+               return false;
+       }
+       attr = tupdesc->attrs[fieldnum - 1];
+       if (attr->attisdropped ||
+               attr->atttypid != expectedtype ||
+               attr->atttypmod != expectedtypmod)
+       {
+               ReleaseTupleDesc(tupdesc);
+               return false;
+       }
+       ReleaseTupleDesc(tupdesc);
+       return true;
+}
+
+
+/*--------------------
+ * eval_const_expressions
+ *
+ * Reduce any recognizably constant subexpressions of the given
+ * expression tree, for example "2 + 2" => "4".  More interestingly,
+ * we can reduce certain boolean expressions even when they contain
+ * non-constant subexpressions: "x OR true" => "true" no matter what
+ * the subexpression x is.     (XXX We assume that no such subexpression
+ * will have important side-effects, which is not necessarily a good
+ * assumption in the presence of user-defined functions; do we need a
+ * pg_proc flag that prevents discarding the execution of a function?)
+ *
+ * We do understand that certain functions may deliver non-constant
+ * results even with constant inputs, "nextval()" being the classic
+ * example.  Functions that are not marked "immutable" in pg_proc
+ * will not be pre-evaluated here, although we will reduce their
+ * arguments as far as possible.
+ *
+ * We assume that the tree has already been type-checked and contains
+ * only operators and functions that are reasonable to try to execute.
+ *
+ * NOTE: the planner assumes that this will always flatten nested AND and
+ * OR clauses into N-argument form.  See comments in prepqual.c.
+ *--------------------
+ */
+Node *
+eval_const_expressions(Node *node)
+{
+       eval_const_expressions_context context;
+
+       context.active_fns = NIL;       /* nothing being recursively simplified */
+       context.case_val = NULL;        /* no CASE being examined */
+       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 extra steps that are taken in this mode are:
+ * 1. Substitute values for Params, where a bound Param value has been made
+ *       available by the caller of planner(), even if the Param isn't marked
+ *       constant.  This effectively means that we plan using the first supplied
+ *       value of the Param.
+ * 2. Fold stable, as well as immutable, functions to constants.
+ *--------------------
+ */
+Node *
+estimate_expression_value(Node *node)
+{
+       eval_const_expressions_context context;
+
+       context.active_fns = NIL;       /* nothing being recursively simplified */
+       context.case_val = NULL;        /* no CASE being examined */
+       context.estimate = true;        /* unsafe transformations OK */
+       return eval_const_expressions_mutator(node, &context);
+}
+
+static Node *
+eval_const_expressions_mutator(Node *node,
+                                                          eval_const_expressions_context *context)
+{
+       if (node == NULL)
                return NULL;
+       if (IsA(node, Param))
+       {
+               Param      *param = (Param *) node;
+
+               /* Look to see if we've been given a value for this Param */
+               if (param->paramkind == PARAM_EXTERN &&
+                       PlannerBoundParamList != NULL &&
+                       param->paramid > 0 &&
+                       param->paramid <= PlannerBoundParamList->numParams)
+               {
+                       ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
+
+                       if (OidIsValid(prm->ptype))
+                       {
+                               /* OK to substitute parameter value? */
+                               if (context->estimate || (prm->pflags & PARAM_FLAG_CONST))
+                               {
+                                       /*
+                                        * Return a Const representing the param value.  Must copy
+                                        * pass-by-ref datatypes, since the Param might be in a
+                                        * memory context shorter-lived than our output plan
+                                        * should be.
+                                        */
+                                       int16           typLen;
+                                       bool            typByVal;
+                                       Datum           pval;
+
+                                       Assert(prm->ptype == param->paramtype);
+                                       get_typlenbyval(param->paramtype, &typLen, &typByVal);
+                                       if (prm->isnull || typByVal)
+                                               pval = prm->value;
+                                       else
+                                               pval = datumCopy(prm->value, typByVal, typLen);
+                                       return (Node *) makeConst(param->paramtype,
+                                                                                         (int) typLen,
+                                                                                         pval,
+                                                                                         prm->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;
@@ -1023,20 +1539,22 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 
                /*
                 * Reduce constants in the FuncExpr's arguments.  We know args is
-                * either NIL or a List node, so we can call
-                * expression_tree_mutator directly rather than recursing to self.
+                * either NIL or a List node, so we can call expression_tree_mutator
+                * directly rather than recursing to self.
                 */
                args = (List *) expression_tree_mutator((Node *) expr->args,
-                                                                                 eval_const_expressions_mutator,
-                                                                                               (void *) active_fns);
+                                                                                         eval_const_expressions_mutator,
+                                                                                               (void *) context);
+
                /*
-                * Code for op/func reduction is pretty bulky, so split it out
-                * as a separate function.
+                * 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;
+
                /*
                 * The expression cannot be simplified any further, so build and
                 * return a replacement FuncExpr node using the possibly-simplified
@@ -1058,26 +1576,40 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
                OpExpr     *newexpr;
 
                /*
-                * Reduce constants in the OpExpr's arguments.  We know args is
-                * either NIL or a List node, so we can call
-                * expression_tree_mutator directly rather than recursing to self.
+                * Reduce constants in the OpExpr's arguments.  We know args is either
+                * NIL or a List node, so we can call expression_tree_mutator directly
+                * rather than recursing to self.
                 */
                args = (List *) expression_tree_mutator((Node *) expr->args,
-                                                                                 eval_const_expressions_mutator,
-                                                                                               (void *) active_fns);
+                                                                                         eval_const_expressions_mutator,
+                                                                                               (void *) context);
+
                /*
-                * Need to get OID of underlying function.  Okay to scribble on
-                * input to this extent.
+                * Need to get OID of underlying function.      Okay to scribble on input
+                * to this extent.
                 */
                set_opfuncid(expr);
+
                /*
-                * Code for op/func reduction is pretty bulky, so split it out
-                * as a separate function.
+                * Code for op/func reduction is pretty bulky, so split it out as a
+                * separate function.
                 */
                simple = simplify_function(expr->opfuncid, expr->opresulttype, args,
-                                                                  true, active_fns);
+                                                                  true, context);
                if (simple)                             /* successfully simplified it */
                        return (Node *) simple;
+
+               /*
+                * If the operator is boolean equality, we know how to simplify cases
+                * involving one constant and one non-constant argument.
+                */
+               if (expr->opno == BooleanEqualOperator)
+               {
+                       simple = simplify_boolean_equality(args);
+                       if (simple)                     /* successfully simplified it */
+                               return (Node *) simple;
+               }
+
                /*
                 * The expression cannot be simplified any further, so build and
                 * return a replacement OpExpr node using the possibly-simplified
@@ -1095,7 +1627,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
        {
                DistinctExpr *expr = (DistinctExpr *) node;
                List       *args;
-               List       *arg;
+               ListCell   *arg;
                bool            has_null_input = false;
                bool            all_null_input = true;
                bool            has_nonconst_input = false;
@@ -1104,17 +1636,16 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 
                /*
                 * Reduce constants in the DistinctExpr's arguments.  We know args is
-                * either NIL or a List node, so we can call
-                * expression_tree_mutator directly rather than recursing to self.
+                * either NIL or a List node, so we can call expression_tree_mutator
+                * directly rather than recursing to self.
                 */
                args = (List *) expression_tree_mutator((Node *) expr->args,
-                                                                                 eval_const_expressions_mutator,
-                                                                                               (void *) active_fns);
+                                                                                         eval_const_expressions_mutator,
+                                                                                               (void *) context);
 
                /*
-                * We must do our own check for NULLs because
-                * DistinctExpr has different results for NULL input
-                * than the underlying operator does.
+                * We must do our own check for NULLs because DistinctExpr has
+                * different results for NULL input than the underlying operator does.
                 */
                foreach(arg, args)
                {
@@ -1132,33 +1663,34 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
                {
                        /* all nulls? then not distinct */
                        if (all_null_input)
-                               return MAKEBOOLCONST(false, false);
+                               return makeBoolConst(false, false);
 
                        /* one null? then distinct */
                        if (has_null_input)
-                               return MAKEBOOLCONST(true, false);
+                               return makeBoolConst(true, false);
 
                        /* otherwise try to evaluate the '=' operator */
                        /* (NOT okay to try to inline it, though!) */
 
                        /*
-                        * Need to get OID of underlying function.  Okay to scribble on
+                        * Need to get OID of underlying function.      Okay to scribble on
                         * input to this extent.
                         */
-                       set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
+                       set_opfuncid((OpExpr *) expr);          /* rely on struct equivalence */
+
                        /*
-                        * Code for op/func reduction is pretty bulky, so split it out
-                        * as a separate function.
+                        * Code for op/func reduction is pretty bulky, so split it out as
+                        * a separate function.
                         */
                        simple = simplify_function(expr->opfuncid, expr->opresulttype,
-                                                                          args, false, active_fns);
+                                                                          args, false, context);
                        if (simple)                     /* successfully simplified it */
                        {
                                /*
                                 * Since the underlying operator is "=", must negate its
                                 * result
                                 */
-                               Const  *csimple = (Const *) simple;
+                               Const      *csimple = (Const *) simple;
 
                                Assert(IsA(csimple, Const));
                                csimple->constvalue =
@@ -1183,140 +1715,79 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
        if (IsA(node, BoolExpr))
        {
                BoolExpr   *expr = (BoolExpr *) node;
-               List       *args;
-               Const      *const_input;
-
-               /*
-                * Reduce constants in the BoolExpr's arguments.  We know args is
-                * either NIL or a List node, so we can call
-                * expression_tree_mutator directly rather than recursing to self.
-                */
-               args = (List *) expression_tree_mutator((Node *) expr->args,
-                                                                                 eval_const_expressions_mutator,
-                                                                                               (void *) active_fns);
 
                switch (expr->boolop)
                {
                        case OR_EXPR:
                                {
-                                       /*----------
-                                        * OR arguments are handled as follows:
-                                        *      non constant: keep
-                                        *      FALSE: drop (does not affect result)
-                                        *      TRUE: force result to TRUE
-                                        *      NULL: keep only one
-                                        * We keep one NULL input because ExecEvalOr returns NULL
-                                        * when no input is TRUE and at least one is NULL.
-                                        *----------
-                                        */
-                                       List       *newargs = NIL;
-                                       List       *arg;
+                                       List       *newargs;
                                        bool            haveNull = false;
                                        bool            forceTrue = false;
 
-                                       foreach(arg, args)
-                                       {
-                                               if (!IsA(lfirst(arg), Const))
-                                               {
-                                                       newargs = lappend(newargs, lfirst(arg));
-                                                       continue;
-                                               }
-                                               const_input = (Const *) lfirst(arg);
-                                               if (const_input->constisnull)
-                                                       haveNull = true;
-                                               else if (DatumGetBool(const_input->constvalue))
-                                                       forceTrue = true;
-                                               /* otherwise, we can drop the constant-false input */
-                                       }
-
-                                       /*
-                                        * We could return TRUE before falling out of the
-                                        * loop, but this coding method will be easier to
-                                        * adapt if we ever add a notion of non-removable
-                                        * functions. We'd need to check all the inputs for
-                                        * non-removability.
-                                        */
+                                       newargs = simplify_or_arguments(expr->args, context,
+                                                                                                       &haveNull, &forceTrue);
                                        if (forceTrue)
-                                               return MAKEBOOLCONST(true, false);
+                                               return makeBoolConst(true, false);
                                        if (haveNull)
-                                               newargs = lappend(newargs, MAKEBOOLCONST(false, true));
+                                               newargs = lappend(newargs, makeBoolConst(false, true));
                                        /* If all the inputs are FALSE, result is FALSE */
                                        if (newargs == NIL)
-                                               return MAKEBOOLCONST(false, false);
+                                               return makeBoolConst(false, false);
                                        /* If only one nonconst-or-NULL input, it's the result */
-                                       if (lnext(newargs) == NIL)
-                                               return (Node *) lfirst(newargs);
+                                       if (list_length(newargs) == 1)
+                                               return (Node *) linitial(newargs);
                                        /* Else we still need an OR node */
                                        return (Node *) make_orclause(newargs);
                                }
                        case AND_EXPR:
                                {
-                                       /*----------
-                                        * AND arguments are handled as follows:
-                                        *      non constant: keep
-                                        *      TRUE: drop (does not affect result)
-                                        *      FALSE: force result to FALSE
-                                        *      NULL: keep only one
-                                        * We keep one NULL input because ExecEvalAnd returns NULL
-                                        * when no input is FALSE and at least one is NULL.
-                                        *----------
-                                        */
-                                       List       *newargs = NIL;
-                                       List       *arg;
+                                       List       *newargs;
                                        bool            haveNull = false;
                                        bool            forceFalse = false;
 
-                                       foreach(arg, args)
-                                       {
-                                               if (!IsA(lfirst(arg), Const))
-                                               {
-                                                       newargs = lappend(newargs, lfirst(arg));
-                                                       continue;
-                                               }
-                                               const_input = (Const *) lfirst(arg);
-                                               if (const_input->constisnull)
-                                                       haveNull = true;
-                                               else if (!DatumGetBool(const_input->constvalue))
-                                                       forceFalse = true;
-                                               /* otherwise, we can drop the constant-true input */
-                                       }
-
-                                       /*
-                                        * We could return FALSE before falling out of the
-                                        * loop, but this coding method will be easier to
-                                        * adapt if we ever add a notion of non-removable
-                                        * functions. We'd need to check all the inputs for
-                                        * non-removability.
-                                        */
+                                       newargs = simplify_and_arguments(expr->args, context,
+                                                                                                        &haveNull, &forceFalse);
                                        if (forceFalse)
-                                               return MAKEBOOLCONST(false, false);
+                                               return makeBoolConst(false, false);
                                        if (haveNull)
-                                               newargs = lappend(newargs, MAKEBOOLCONST(false, true));
+                                               newargs = lappend(newargs, makeBoolConst(false, true));
                                        /* If all the inputs are TRUE, result is TRUE */
                                        if (newargs == NIL)
-                                               return MAKEBOOLCONST(true, false);
+                                               return makeBoolConst(true, false);
                                        /* If only one nonconst-or-NULL input, it's the result */
-                                       if (lnext(newargs) == NIL)
-                                               return (Node *) lfirst(newargs);
+                                       if (list_length(newargs) == 1)
+                                               return (Node *) linitial(newargs);
                                        /* Else we still need an AND node */
                                        return (Node *) make_andclause(newargs);
                                }
                        case NOT_EXPR:
-                               Assert(length(args) == 1);
-                               if (IsA(lfirst(args), Const))
                                {
-                                       const_input = (Const *) lfirst(args);
-                                       /* NOT NULL => NULL */
-                                       if (const_input->constisnull)
-                                               return MAKEBOOLCONST(false, true);
-                                       /* otherwise pretty easy */
-                                       return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
-                                                                                false);
+                                       Node       *arg;
+
+                                       Assert(list_length(expr->args) == 1);
+                                       arg = eval_const_expressions_mutator(linitial(expr->args),
+                                                                                                                context);
+                                       if (IsA(arg, Const))
+                                       {
+                                               Const      *const_input = (Const *) arg;
+
+                                               /* NOT NULL => NULL */
+                                               if (const_input->constisnull)
+                                                       return makeBoolConst(false, true);
+                                               /* otherwise pretty easy */
+                                               return makeBoolConst(!DatumGetBool(const_input->constvalue),
+                                                                                        false);
+                                       }
+                                       else if (not_clause(arg))
+                                       {
+                                               /* Cancel NOT/NOT */
+                                               return (Node *) get_notclausearg((Expr *) arg);
+                                       }
+                                       /* Else we still need a NOT node */
+                                       return (Node *) make_notclause((Expr *) arg);
                                }
-                               /* Else we still need a NOT node */
-                               return (Node *) make_notclause(lfirst(args));
                        default:
-                               elog(ERROR, "eval_const_expressions: unexpected boolop %d",
+                               elog(ERROR, "unrecognized boolop: %d",
                                         (int) expr->boolop);
                                break;
                }
@@ -1324,30 +1795,29 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
        if (IsA(node, SubPlan))
        {
                /*
-                * Return a SubPlan unchanged --- too late to do anything
-                * with it.
+                * Return a SubPlan unchanged --- too late to do anything with it.
                 *
-                * XXX should we elog() here instead?  Probably this routine
-                * should never be invoked after SubPlan creation.
+                * XXX should we ereport() here instead?  Probably this routine should
+                * never be invoked after SubPlan creation.
                 */
                return node;
        }
        if (IsA(node, RelabelType))
        {
                /*
-                * If we can simplify the input to a constant, then we don't need
-                * the RelabelType node anymore: just change the type field of the
-                * Const node.  Otherwise, must copy the RelabelType node.
+                * If we can simplify the input to a constant, then we don't need the
+                * RelabelType node anymore: just change the type field of the Const
+                * node.  Otherwise, must copy the RelabelType node.
                 */
                RelabelType *relabel = (RelabelType *) node;
                Node       *arg;
 
                arg = eval_const_expressions_mutator((Node *) relabel->arg,
-                                                                                        active_fns);
+                                                                                        context);
 
                /*
-                * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we
-                * can discard all but the top one.
+                * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we can
+                * discard all but the top one.
                 */
                while (arg && IsA(arg, RelabelType))
                        arg = (Node *) ((RelabelType *) arg)->arg;
@@ -1359,10 +1829,9 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
                        con->consttype = relabel->resulttype;
 
                        /*
-                        * relabel's resulttypmod is discarded, which is OK for now;
-                        * if the type actually needs a runtime length coercion then
-                        * there should be a function call to do it just above this
-                        * node.
+                        * relabel's resulttypmod is discarded, which is OK for now; if
+                        * the type actually needs a runtime length coercion then there
+                        * should be a function call to do it just above this node.
                         */
                        return (Node *) con;
                }
@@ -1378,7 +1847,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
        }
        if (IsA(node, CaseExpr))
        {
-
                /*----------
                 * CASE expressions can be simplified if there are constant
                 * condition clauses:
@@ -1388,81 +1856,136 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
                 * simplify the entire CASE to that alternative's expression.
                 * If there are no non-FALSE alternatives, we simplify the entire
                 * CASE to the default result (ELSE result).
+                *
+                * If we have a simple-form CASE with constant test expression,
+                * we substitute the constant value for contained CaseTestExpr
+                * placeholder nodes, so that we have the opportunity to reduce
+                * constant test conditions.  For example this allows
+                *              CASE 0 WHEN 0 THEN 1 ELSE 1/0 END
+                * to reduce to 1 rather than drawing a divide-by-0 error.
                 *----------
                 */
                CaseExpr   *caseexpr = (CaseExpr *) node;
                CaseExpr   *newcase;
-               List       *newargs = NIL;
-               Node       *defresult;
-               Const      *const_input;
-               List       *arg;
+               Node       *save_case_val;
+               Node       *newarg;
+               List       *newargs;
+               bool            const_true_cond;
+               Node       *defresult = NULL;
+               ListCell   *arg;
+
+               /* Simplify the test expression, if any */
+               newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
+                                                                                               context);
+
+               /* Set up for contained CaseTestExpr nodes */
+               save_case_val = context->case_val;
+               if (newarg && IsA(newarg, Const))
+                       context->case_val = newarg;
+               else
+                       context->case_val = NULL;
 
+               /* Simplify the WHEN clauses */
+               newargs = NIL;
+               const_true_cond = false;
                foreach(arg, caseexpr->args)
                {
-                       /* Simplify this alternative's condition and result */
-                       CaseWhen   *casewhen = (CaseWhen *)
-                       expression_tree_mutator((Node *) lfirst(arg),
-                                                                       eval_const_expressions_mutator,
-                                                                       (void *) active_fns);
+                       CaseWhen   *oldcasewhen = (CaseWhen *) lfirst(arg);
+                       Node       *casecond;
+                       Node       *caseresult;
 
-                       Assert(IsA(casewhen, CaseWhen));
-                       if (casewhen->expr == NULL ||
-                               !IsA(casewhen->expr, Const))
-                       {
-                               newargs = lappend(newargs, casewhen);
-                               continue;
-                       }
-                       const_input = (Const *) casewhen->expr;
-                       if (const_input->constisnull ||
-                               !DatumGetBool(const_input->constvalue))
-                               continue;               /* drop alternative with FALSE condition */
+                       Assert(IsA(oldcasewhen, CaseWhen));
+
+                       /* Simplify this alternative's test condition */
+                       casecond =
+                               eval_const_expressions_mutator((Node *) oldcasewhen->expr,
+                                                                                          context);
 
                        /*
-                        * Found a TRUE condition.      If it's the first (un-dropped)
-                        * alternative, the CASE reduces to just this alternative.
+                        * If the test condition is constant FALSE (or NULL), then drop
+                        * this WHEN clause completely, without processing the result.
                         */
-                       if (newargs == NIL)
-                               return (Node *) casewhen->result;
+                       if (casecond && IsA(casecond, Const))
+                       {
+                               Const      *const_input = (Const *) casecond;
+
+                               if (const_input->constisnull ||
+                                       !DatumGetBool(const_input->constvalue))
+                                       continue;       /* drop alternative with FALSE condition */
+                               /* Else it's constant TRUE */
+                               const_true_cond = true;
+                       }
+
+                       /* Simplify this alternative's result value */
+                       caseresult =
+                               eval_const_expressions_mutator((Node *) oldcasewhen->result,
+                                                                                          context);
+
+                       /* If non-constant test condition, emit a new WHEN node */
+                       if (!const_true_cond)
+                       {
+                               CaseWhen   *newcasewhen = makeNode(CaseWhen);
+
+                               newcasewhen->expr = (Expr *) casecond;
+                               newcasewhen->result = (Expr *) caseresult;
+                               newargs = lappend(newargs, newcasewhen);
+                               continue;
+                       }
 
                        /*
-                        * Otherwise, add it to the list, and drop all the rest.
+                        * Found a TRUE condition, so none of the remaining alternatives
+                        * can be reached.      We treat the result as the default result.
                         */
-                       newargs = lappend(newargs, casewhen);
+                       defresult = caseresult;
                        break;
                }
 
-               /* Simplify the default result */
-               defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult,
-                                                                                                  active_fns);
+               /* Simplify the default result, unless we replaced it above */
+               if (!const_true_cond)
+                       defresult =
+                               eval_const_expressions_mutator((Node *) caseexpr->defresult,
+                                                                                          context);
 
-               /*
-                * If no non-FALSE alternatives, CASE reduces to the default
-                * result
-                */
+               context->case_val = save_case_val;
+
+               /* If no non-FALSE alternatives, CASE reduces to the default result */
                if (newargs == NIL)
                        return defresult;
                /* Otherwise we need a new CASE node */
                newcase = makeNode(CaseExpr);
                newcase->casetype = caseexpr->casetype;
-               newcase->arg = NULL;
+               newcase->arg = (Expr *) newarg;
                newcase->args = newargs;
                newcase->defresult = (Expr *) defresult;
                return (Node *) newcase;
        }
+       if (IsA(node, CaseTestExpr))
+       {
+               /*
+                * If we know a constant test value for the current CASE construct,
+                * substitute it for the placeholder.  Else just return the
+                * placeholder as-is.
+                */
+               if (context->case_val)
+                       return copyObject(context->case_val);
+               else
+                       return copyObject(node);
+       }
        if (IsA(node, ArrayExpr))
        {
-               ArrayExpr *arrayexpr = (ArrayExpr *) node;
-               ArrayExpr *newarray;
-               bool all_const = true;
-               List *newelems = NIL;
-               List *element;
+               ArrayExpr  *arrayexpr = (ArrayExpr *) node;
+               ArrayExpr  *newarray;
+               bool            all_const = true;
+               List       *newelems;
+               ListCell   *element;
 
+               newelems = NIL;
                foreach(element, arrayexpr->elements)
                {
-                       Node *e;
+                       Node       *e;
 
                        e = eval_const_expressions_mutator((Node *) lfirst(element),
-                                                                                          active_fns);
+                                                                                          context);
                        if (!IsA(e, Const))
                                all_const = false;
                        newelems = lappend(newelems, e);
@@ -1472,79 +1995,506 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
                newarray->array_typeid = arrayexpr->array_typeid;
                newarray->element_typeid = arrayexpr->element_typeid;
                newarray->elements = newelems;
-               newarray->ndims = arrayexpr->ndims;
+               newarray->multidims = arrayexpr->multidims;
 
                if (all_const)
                        return (Node *) evaluate_expr((Expr *) newarray,
                                                                                  newarray->array_typeid);
 
-               return (Node *) newarray;
-       }
-       if (IsA(node, CoalesceExpr))
+               return (Node *) newarray;
+       }
+       if (IsA(node, CoalesceExpr))
+       {
+               CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
+               CoalesceExpr *newcoalesce;
+               List       *newargs;
+               ListCell   *arg;
+
+               newargs = NIL;
+               foreach(arg, coalesceexpr->args)
+               {
+                       Node       *e;
+
+                       e = eval_const_expressions_mutator((Node *) lfirst(arg),
+                                                                                          context);
+
+                       /*
+                        * We can remove null constants from the list. For a non-null
+                        * constant, if it has not been preceded by any other
+                        * non-null-constant expressions then that is the result.
+                        */
+                       if (IsA(e, Const))
+                       {
+                               if (((Const *) e)->constisnull)
+                                       continue;       /* drop null constant */
+                               if (newargs == NIL)
+                                       return e;       /* first expr */
+                       }
+                       newargs = lappend(newargs, e);
+               }
+
+               /* If all the arguments were constant null, the result is just null */
+               if (newargs == NIL)
+                       return (Node *) makeNullConst(coalesceexpr->coalescetype);
+
+               newcoalesce = makeNode(CoalesceExpr);
+               newcoalesce->coalescetype = coalesceexpr->coalescetype;
+               newcoalesce->args = newargs;
+               return (Node *) newcoalesce;
+       }
+       if (IsA(node, FieldSelect))
+       {
+               /*
+                * We can optimize field selection from a whole-row Var into a simple
+                * Var.  (This case won't be generated directly by the parser, because
+                * ParseComplexProjection short-circuits it. But it can arise while
+                * simplifying functions.)      Also, we can optimize field selection from
+                * a RowExpr construct.
+                *
+                * We must however check that the declared type of the field is still
+                * the same as when the FieldSelect was created --- this can change if
+                * someone did ALTER COLUMN TYPE on the rowtype.
+                */
+               FieldSelect *fselect = (FieldSelect *) node;
+               FieldSelect *newfselect;
+               Node       *arg;
+
+               arg = eval_const_expressions_mutator((Node *) fselect->arg,
+                                                                                        context);
+               if (arg && IsA(arg, Var) &&
+                       ((Var *) arg)->varattno == InvalidAttrNumber)
+               {
+                       if (rowtype_field_matches(((Var *) arg)->vartype,
+                                                                         fselect->fieldnum,
+                                                                         fselect->resulttype,
+                                                                         fselect->resulttypmod))
+                               return (Node *) makeVar(((Var *) arg)->varno,
+                                                                               fselect->fieldnum,
+                                                                               fselect->resulttype,
+                                                                               fselect->resulttypmod,
+                                                                               ((Var *) arg)->varlevelsup);
+               }
+               if (arg && IsA(arg, RowExpr))
+               {
+                       RowExpr    *rowexpr = (RowExpr *) arg;
+
+                       if (fselect->fieldnum > 0 &&
+                               fselect->fieldnum <= list_length(rowexpr->args))
+                       {
+                               Node       *fld = (Node *) list_nth(rowexpr->args,
+                                                                                                       fselect->fieldnum - 1);
+
+                               if (rowtype_field_matches(rowexpr->row_typeid,
+                                                                                 fselect->fieldnum,
+                                                                                 fselect->resulttype,
+                                                                                 fselect->resulttypmod) &&
+                                       fselect->resulttype == exprType(fld) &&
+                                       fselect->resulttypmod == exprTypmod(fld))
+                                       return fld;
+                       }
+               }
+               newfselect = makeNode(FieldSelect);
+               newfselect->arg = (Expr *) arg;
+               newfselect->fieldnum = fselect->fieldnum;
+               newfselect->resulttype = fselect->resulttype;
+               newfselect->resulttypmod = fselect->resulttypmod;
+               return (Node *) newfselect;
+       }
+       if (IsA(node, NullTest))
+       {
+               NullTest   *ntest = (NullTest *) node;
+               NullTest   *newntest;
+               Node       *arg;
+
+               arg = eval_const_expressions_mutator((Node *) ntest->arg,
+                                                                                        context);
+               if (arg && IsA(arg, RowExpr))
+               {
+                       RowExpr    *rarg = (RowExpr *) arg;
+                       List       *newargs = NIL;
+                       ListCell   *l;
+
+                       /*
+                        * We break ROW(...) IS [NOT] NULL into separate tests on its
+                        * component fields.  This form is usually more efficient to
+                        * evaluate, as well as being more amenable to optimization.
+                        */
+                       foreach(l, rarg->args)
+                       {
+                               Node       *relem = (Node *) lfirst(l);
+
+                               /*
+                                * A constant field refutes the whole NullTest if it's of the
+                                * wrong nullness; else we can discard it.
+                                */
+                               if (relem && IsA(relem, Const))
+                               {
+                                       Const      *carg = (Const *) relem;
+
+                                       if (carg->constisnull ?
+                                               (ntest->nulltesttype == IS_NOT_NULL) :
+                                               (ntest->nulltesttype == IS_NULL))
+                                               return makeBoolConst(false, false);
+                                       continue;
+                               }
+                               newntest = makeNode(NullTest);
+                               newntest->arg = (Expr *) relem;
+                               newntest->nulltesttype = ntest->nulltesttype;
+                               newargs = lappend(newargs, newntest);
+                       }
+                       /* If all the inputs were constants, result is TRUE */
+                       if (newargs == NIL)
+                               return makeBoolConst(true, false);
+                       /* If only one nonconst input, it's the result */
+                       if (list_length(newargs) == 1)
+                               return (Node *) linitial(newargs);
+                       /* Else we need an AND node */
+                       return (Node *) make_andclause(newargs);
+               }
+               if (arg && IsA(arg, Const))
+               {
+                       Const      *carg = (Const *) arg;
+                       bool            result;
+
+                       switch (ntest->nulltesttype)
+                       {
+                               case IS_NULL:
+                                       result = carg->constisnull;
+                                       break;
+                               case IS_NOT_NULL:
+                                       result = !carg->constisnull;
+                                       break;
+                               default:
+                                       elog(ERROR, "unrecognized nulltesttype: %d",
+                                                (int) ntest->nulltesttype);
+                                       result = false;         /* keep compiler quiet */
+                                       break;
+                       }
+
+                       return makeBoolConst(result, false);
+               }
+
+               newntest = makeNode(NullTest);
+               newntest->arg = (Expr *) arg;
+               newntest->nulltesttype = ntest->nulltesttype;
+               return (Node *) newntest;
+       }
+       if (IsA(node, BooleanTest))
+       {
+               BooleanTest *btest = (BooleanTest *) node;
+               BooleanTest *newbtest;
+               Node       *arg;
+
+               arg = eval_const_expressions_mutator((Node *) btest->arg,
+                                                                                        context);
+               if (arg && IsA(arg, Const))
+               {
+                       Const      *carg = (Const *) arg;
+                       bool            result;
+
+                       switch (btest->booltesttype)
+                       {
+                               case IS_TRUE:
+                                       result = (!carg->constisnull &&
+                                                         DatumGetBool(carg->constvalue));
+                                       break;
+                               case IS_NOT_TRUE:
+                                       result = (carg->constisnull ||
+                                                         !DatumGetBool(carg->constvalue));
+                                       break;
+                               case IS_FALSE:
+                                       result = (!carg->constisnull &&
+                                                         !DatumGetBool(carg->constvalue));
+                                       break;
+                               case IS_NOT_FALSE:
+                                       result = (carg->constisnull ||
+                                                         DatumGetBool(carg->constvalue));
+                                       break;
+                               case IS_UNKNOWN:
+                                       result = carg->constisnull;
+                                       break;
+                               case IS_NOT_UNKNOWN:
+                                       result = !carg->constisnull;
+                                       break;
+                               default:
+                                       elog(ERROR, "unrecognized booltesttype: %d",
+                                                (int) btest->booltesttype);
+                                       result = false;         /* keep compiler quiet */
+                                       break;
+                       }
+
+                       return makeBoolConst(result, false);
+               }
+
+               newbtest = makeNode(BooleanTest);
+               newbtest->arg = (Expr *) arg;
+               newbtest->booltesttype = btest->booltesttype;
+               return (Node *) newbtest;
+       }
+
+       /*
+        * For any node type not handled above, we recurse using
+        * expression_tree_mutator, which will copy the node unchanged but try to
+        * simplify its arguments (if any) using this routine. For example: we
+        * cannot eliminate an ArrayRef node, but we might be able to simplify
+        * constant expressions in its subscripts.
+        */
+       return expression_tree_mutator(node, eval_const_expressions_mutator,
+                                                                  (void *) context);
+}
+
+/*
+ * Subroutine for eval_const_expressions: process arguments of an OR clause
+ *
+ * This includes flattening of nested ORs as well as recursion to
+ * eval_const_expressions to simplify the OR arguments.
+ *
+ * After simplification, OR arguments are handled as follows:
+ *             non constant: keep
+ *             FALSE: drop (does not affect result)
+ *             TRUE: force result to TRUE
+ *             NULL: keep only one
+ * We must keep one NULL input because ExecEvalOr returns NULL when no input
+ * is TRUE and at least one is NULL.  We don't actually include the NULL
+ * here, that's supposed to be done by the caller.
+ *
+ * The output arguments *haveNull and *forceTrue must be initialized FALSE
+ * by the caller.  They will be set TRUE if a null constant or true constant,
+ * respectively, is detected anywhere in the argument list.
+ */
+static List *
+simplify_or_arguments(List *args,
+                                         eval_const_expressions_context *context,
+                                         bool *haveNull, bool *forceTrue)
+{
+       List       *newargs = NIL;
+       List       *unprocessed_args;
+
+       /*
+        * Since the parser considers OR to be a binary operator, long OR lists
+        * become deeply nested expressions.  We must flatten these into long
+        * argument lists of a single OR operator.      To avoid blowing out the stack
+        * with recursion of eval_const_expressions, we resort to some tenseness
+        * here: we keep a list of not-yet-processed inputs, and handle flattening
+        * of nested ORs by prepending to the to-do list instead of recursing.
+        */
+       unprocessed_args = list_copy(args);
+       while (unprocessed_args)
+       {
+               Node       *arg = (Node *) linitial(unprocessed_args);
+
+               unprocessed_args = list_delete_first(unprocessed_args);
+
+               /* flatten nested ORs as per above comment */
+               if (or_clause(arg))
+               {
+                       List       *subargs = list_copy(((BoolExpr *) arg)->args);
+
+                       /* overly tense code to avoid leaking unused list header */
+                       if (!unprocessed_args)
+                               unprocessed_args = subargs;
+                       else
+                       {
+                               List       *oldhdr = unprocessed_args;
+
+                               unprocessed_args = list_concat(subargs, unprocessed_args);
+                               pfree(oldhdr);
+                       }
+                       continue;
+               }
+
+               /* If it's not an OR, simplify it */
+               arg = eval_const_expressions_mutator(arg, context);
+
+               /*
+                * It is unlikely but not impossible for simplification of a non-OR
+                * clause to produce an OR.  Recheck, but don't be too tense about it
+                * since it's not a mainstream case. In particular we don't worry
+                * about const-simplifying the input twice.
+                */
+               if (or_clause(arg))
+               {
+                       List       *subargs = list_copy(((BoolExpr *) arg)->args);
+
+                       unprocessed_args = list_concat(subargs, unprocessed_args);
+                       continue;
+               }
+
+               /*
+                * OK, we have a const-simplified non-OR argument.      Process it per
+                * comments above.
+                */
+               if (IsA(arg, Const))
+               {
+                       Const      *const_input = (Const *) arg;
+
+                       if (const_input->constisnull)
+                               *haveNull = true;
+                       else if (DatumGetBool(const_input->constvalue))
+                       {
+                               *forceTrue = true;
+
+                               /*
+                                * Once we detect a TRUE result we can just exit the loop
+                                * immediately.  However, if we ever add a notion of
+                                * non-removable functions, we'd need to keep scanning.
+                                */
+                               return NIL;
+                       }
+                       /* otherwise, we can drop the constant-false input */
+                       continue;
+               }
+
+               /* else emit the simplified arg into the result list */
+               newargs = lappend(newargs, arg);
+       }
+
+       return newargs;
+}
+
+/*
+ * Subroutine for eval_const_expressions: process arguments of an AND clause
+ *
+ * This includes flattening of nested ANDs as well as recursion to
+ * eval_const_expressions to simplify the AND arguments.
+ *
+ * After simplification, AND arguments are handled as follows:
+ *             non constant: keep
+ *             TRUE: drop (does not affect result)
+ *             FALSE: force result to FALSE
+ *             NULL: keep only one
+ * We must keep one NULL input because ExecEvalAnd returns NULL when no input
+ * is FALSE and at least one is NULL.  We don't actually include the NULL
+ * here, that's supposed to be done by the caller.
+ *
+ * The output arguments *haveNull and *forceFalse must be initialized FALSE
+ * by the caller.  They will be set TRUE if a null constant or false constant,
+ * respectively, is detected anywhere in the argument list.
+ */
+static List *
+simplify_and_arguments(List *args,
+                                          eval_const_expressions_context *context,
+                                          bool *haveNull, bool *forceFalse)
+{
+       List       *newargs = NIL;
+       List       *unprocessed_args;
+
+       /* See comments in simplify_or_arguments */
+       unprocessed_args = list_copy(args);
+       while (unprocessed_args)
        {
-               CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
-               CoalesceExpr *newcoalesce;
-               List *newargs = NIL;
-               List *arg;
+               Node       *arg = (Node *) linitial(unprocessed_args);
 
-               foreach(arg, coalesceexpr->args)
+               unprocessed_args = list_delete_first(unprocessed_args);
+
+               /* flatten nested ANDs as per above comment */
+               if (and_clause(arg))
                {
-                       Node *e;
+                       List       *subargs = list_copy(((BoolExpr *) arg)->args);
 
-                       e = eval_const_expressions_mutator((Node *) lfirst(arg),
-                                                                                          active_fns);
-                       /* 
-                        * We can remove null constants from the list.
-                        * For a non-null constant, if it has not been preceded by any
-                        * other non-null-constant expressions then that is the result.
-                        */
-                       if (IsA(e, Const))
+                       /* overly tense code to avoid leaking unused list header */
+                       if (!unprocessed_args)
+                               unprocessed_args = subargs;
+                       else
                        {
-                               if (((Const *) e)->constisnull)
-                                       continue;       /* drop null constant */
-                               if (newargs == NIL)
-                                       return e;       /* first expr */
+                               List       *oldhdr = unprocessed_args;
+
+                               unprocessed_args = list_concat(subargs, unprocessed_args);
+                               pfree(oldhdr);
                        }
-                       newargs = lappend(newargs, e);
+                       continue;
                }
 
-               newcoalesce = makeNode(CoalesceExpr);
-               newcoalesce->coalescetype = coalesceexpr->coalescetype;
-               newcoalesce->args = newargs;
-               return (Node *) newcoalesce;
-       }
-       if (IsA(node, FieldSelect))
-       {
+               /* If it's not an AND, simplify it */
+               arg = eval_const_expressions_mutator(arg, context);
+
                /*
-                * We can optimize field selection from a whole-row Var into a
-                * simple Var.  (This case won't be generated directly by the
-                * parser, because ParseComplexProjection short-circuits it.
-                * But it can arise while simplifying functions.)  If the argument
-                * isn't a whole-row Var, just fall through to do generic processing.
+                * It is unlikely but not impossible for simplification of a non-AND
+                * clause to produce an AND.  Recheck, but don't be too tense about it
+                * since it's not a mainstream case. In particular we don't worry
+                * about const-simplifying the input twice.
                 */
-               FieldSelect *fselect = (FieldSelect *) node;
-               Var                *argvar = (Var *) fselect->arg;
+               if (and_clause(arg))
+               {
+                       List       *subargs = list_copy(((BoolExpr *) arg)->args);
+
+                       unprocessed_args = list_concat(subargs, unprocessed_args);
+                       continue;
+               }
 
-               if (argvar && IsA(argvar, Var) &&
-                       argvar->varattno == InvalidAttrNumber)
+               /*
+                * OK, we have a const-simplified non-AND argument.  Process it per
+                * comments above.
+                */
+               if (IsA(arg, Const))
                {
-                       return (Node *) makeVar(argvar->varno,
-                                                                       fselect->fieldnum,
-                                                                       fselect->resulttype,
-                                                                       fselect->resulttypmod,
-                                                                       argvar->varlevelsup);
+                       Const      *const_input = (Const *) arg;
+
+                       if (const_input->constisnull)
+                               *haveNull = true;
+                       else if (!DatumGetBool(const_input->constvalue))
+                       {
+                               *forceFalse = true;
+
+                               /*
+                                * Once we detect a FALSE result we can just exit the loop
+                                * immediately.  However, if we ever add a notion of
+                                * non-removable functions, we'd need to keep scanning.
+                                */
+                               return NIL;
+                       }
+                       /* otherwise, we can drop the constant-true input */
+                       continue;
                }
+
+               /* else emit the simplified arg into the result list */
+               newargs = lappend(newargs, arg);
        }
 
-       /*
-        * For any node type not handled above, we recurse using
-        * expression_tree_mutator, which will copy the node unchanged but try
-        * to simplify its arguments (if any) using this routine. For example:
-        * we cannot eliminate an ArrayRef node, but we might be able to
-        * simplify constant expressions in its subscripts.
-        */
-       return expression_tree_mutator(node, eval_const_expressions_mutator,
-                                                                  (void *) active_fns);
+       return newargs;
+}
+
+/*
+ * Subroutine for eval_const_expressions: try to simplify boolean equality
+ *
+ * Input is the list of simplified arguments to the operator.
+ * Returns a simplified expression if successful, or NULL if cannot
+ * simplify the expression.
+ *
+ * The idea here is to reduce "x = true" to "x" and "x = false" to "NOT x".
+ * This is only marginally useful in itself, but doing it in constant folding
+ * ensures that we will recognize the two forms as being equivalent in, for
+ * example, partial index matching.
+ *
+ * We come here only if simplify_function has failed; therefore we cannot
+ * see two constant inputs, nor a constant-NULL input.
+ */
+static Expr *
+simplify_boolean_equality(List *args)
+{
+       Expr       *leftop;
+       Expr       *rightop;
+
+       Assert(list_length(args) == 2);
+       leftop = linitial(args);
+       rightop = lsecond(args);
+       if (leftop && IsA(leftop, Const))
+       {
+               Assert(!((Const *) leftop)->constisnull);
+               if (DatumGetBool(((Const *) leftop)->constvalue))
+                       return rightop;         /* true = foo */
+               else
+                       return make_notclause(rightop);         /* false = foo */
+       }
+       if (rightop && IsA(rightop, Const))
+       {
+               Assert(!((Const *) rightop)->constisnull);
+               if (DatumGetBool(((Const *) rightop)->constvalue))
+                       return leftop;          /* foo = true */
+               else
+                       return make_notclause(leftop);          /* foo = false */
+       }
+       return NULL;
 }
 
 /*
@@ -1553,14 +2503,15 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
  *
  * 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;
@@ -1577,13 +2528,14 @@ simplify_function(Oid funcid, Oid result_type, List *args,
                                                                ObjectIdGetDatum(funcid),
                                                                0, 0, 0);
        if (!HeapTupleIsValid(func_tuple))
-               elog(ERROR, "Function OID %u does not exist", funcid);
+               elog(ERROR, "cache lookup failed for function %u", funcid);
 
-       newexpr = evaluate_function(funcid, result_type, args, func_tuple);
+       newexpr = evaluate_function(funcid, result_type, args,
+                                                               func_tuple, context);
 
        if (!newexpr && allow_inline)
                newexpr = inline_function(funcid, result_type, args,
-                                                                 func_tuple, active_fns);
+                                                                 func_tuple, context);
 
        ReleaseSysCache(func_tuple);
 
@@ -1595,19 +2547,21 @@ simplify_function(Oid funcid, Oid result_type, List *args,
  *
  * We can do this if the function is strict and has any constant-null inputs
  * (just return a null constant), or if the function is immutable and has all
- * constant inputs (call it and return the result as a Const node).
+ * constant inputs (call it and return the result as a Const node).  In
+ * estimation mode we are willing to pre-evaluate stable functions too.
  *
  * Returns a simplified expression if successful, or NULL if cannot
  * simplify the function.
  */
 static Expr *
 evaluate_function(Oid funcid, Oid result_type, List *args,
-                                 HeapTuple func_tuple)
+                                 HeapTuple func_tuple,
+                                 eval_const_expressions_context *context)
 {
        Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
        bool            has_nonconst_input = false;
        bool            has_null_input = false;
-       List       *arg;
+       ListCell   *arg;
        FuncExpr   *newexpr;
 
        /*
@@ -1616,6 +2570,20 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
        if (funcform->proretset)
                return NULL;
 
+       /*
+        * Can't simplify if it returns RECORD.  The immediate problem is that it
+        * will be needing an expected tupdesc which we can't supply here.
+        *
+        * In the case where it has OUT parameters, it could get by without an
+        * expected tupdesc, but we still have issues: get_expr_result_type()
+        * doesn't know how to extract type info from a RECORD constant, and in
+        * the case of a NULL function result there doesn't seem to be any clean
+        * way to fix that.  In view of the likelihood of there being still other
+        * gotchas, seems best to leave the function call unreduced.
+        */
+       if (funcform->prorettype == RECORDOID)
+               return NULL;
+
        /*
         * Check for constant inputs and especially constant-NULL inputs.
         */
@@ -1628,21 +2596,34 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
        }
 
        /*
-        * If the function is strict and has a constant-NULL input, it will
-        * never be called at all, so we can replace the call by a NULL
-        * constant, even if there are other inputs that aren't constant,
-        * and even if the function is not otherwise immutable.
+        * If the function is strict and has a constant-NULL input, it will never
+        * be called at all, so we can replace the call by a NULL constant, even
+        * if there are other inputs that aren't constant, and even if the
+        * function is not otherwise immutable.
         */
        if (funcform->proisstrict && has_null_input)
                return (Expr *) makeNullConst(result_type);
 
        /*
-        * Otherwise, can simplify only if the function is immutable and
-        * all inputs are constants. (For a non-strict function, constant NULL
-        * inputs are treated the same as constant non-NULL inputs.)
+        * Otherwise, can simplify only if all inputs are constants. (For a
+        * non-strict function, constant NULL inputs are treated the same as
+        * constant non-NULL inputs.)
+        */
+       if (has_nonconst_input)
+               return NULL;
+
+       /*
+        * Ordinarily we are only allowed to simplify immutable functions. But for
+        * purposes of estimation, we consider it okay to simplify functions that
+        * are merely stable; the risk that the result might change from planning
+        * time to execution time is worth taking in preference to not being able
+        * to estimate the value at all.
         */
-       if (funcform->provolatile != PROVOLATILE_IMMUTABLE ||
-               has_nonconst_input)
+       if (funcform->provolatile == PROVOLATILE_IMMUTABLE)
+                /* okay */ ;
+       else if (context->estimate && funcform->provolatile == PROVOLATILE_STABLE)
+                /* okay */ ;
+       else
                return NULL;
 
        /*
@@ -1654,7 +2635,7 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
        newexpr->funcid = funcid;
        newexpr->funcresulttype = result_type;
        newexpr->funcretset = false;
-       newexpr->funcformat = COERCE_EXPLICIT_CALL;     /* doesn't matter */
+       newexpr->funcformat = COERCE_DONTCARE;          /* doesn't matter */
        newexpr->args = args;
 
        return evaluate_expr((Expr *) newexpr, result_type);
@@ -1673,8 +2654,12 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
  * so we keep track of which functions we are already expanding and
  * do not re-expand them.  Also, if a parameter is used more than once
  * in the SQL-function body, we require it not to contain any volatile
- * functions or sublinks --- volatiles might deliver inconsistent answers,
- * and subplans might be unreasonably expensive to evaluate multiple times.
+ * functions (volatiles might deliver inconsistent answers) nor to be
+ * unreasonably expensive to evaluate. The expensiveness check not only
+ * prevents us from doing multiple evaluations of an expensive parameter
+ * at runtime, but is a safety value to limit growth of an expression due
+ * to repeated inlining.
+ *
  * We must also beware of changing the volatility or strictness status of
  * functions by inlining them.
  *
@@ -1683,41 +2668,38 @@ 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);
-       char            result_typtype;
+       bool            polymorphic = false;
+       Oid                *argtypes;
        char       *src;
        Datum           tmp;
        bool            isNull;
        MemoryContext oldcxt;
        MemoryContext mycxt;
+       ErrorContextCallback sqlerrcontext;
        List       *raw_parsetree_list;
        List       *querytree_list;
        Query      *querytree;
        Node       *newexpr;
        int                *usecounts;
-       List       *arg;
+       ListCell   *arg;
        int                     i;
 
        /*
-        * Forget it if the function is not SQL-language or has other
-        * showstopper properties.  (The nargs check is just paranoia.)
+        * Forget it if the function is not SQL-language or has other showstopper
+        * properties.  (The nargs check is just paranoia.)
         */
        if (funcform->prolang != SQLlanguageId ||
                funcform->prosecdef ||
                funcform->proretset ||
-               funcform->pronargs != length(args))
-               return NULL;
-
-       /* Forget it if declared return type is tuple or void */
-       result_typtype = get_typtype(funcform->prorettype);
-       if (result_typtype != 'b' &&
-               result_typtype != 'd')
+               funcform->pronargs != list_length(args))
                return NULL;
 
        /* Check for recursive function, and give up trying to expand if so */
-       if (oidMember(funcid, active_fns))
+       if (list_member_oid(context->active_fns, funcid))
                return NULL;
 
        /* Check permission to call function (fail later, if not) */
@@ -1725,8 +2707,17 @@ inline_function(Oid funcid, Oid result_type, List *args,
                return NULL;
 
        /*
-        * Make a temporary memory context, so that we don't leak all the
-        * stuff that parsing might create.
+        * Setup error traceback support for ereport().  This is so that we can
+        * finger the function that bad information came from.
+        */
+       sqlerrcontext.callback = sql_inline_error_callback;
+       sqlerrcontext.arg = func_tuple;
+       sqlerrcontext.previous = error_context_stack;
+       error_context_stack = &sqlerrcontext;
+
+       /*
+        * Make a temporary memory context, so that we don't leak all the stuff
+        * that parsing might create.
         */
        mycxt = AllocSetContextCreate(CurrentMemoryContext,
                                                                  "inline_function",
@@ -1735,41 +2726,56 @@ inline_function(Oid funcid, Oid result_type, List *args,
                                                                  ALLOCSET_DEFAULT_MAXSIZE);
        oldcxt = MemoryContextSwitchTo(mycxt);
 
+       /* Check for polymorphic arguments, and substitute actual arg types */
+       argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
+       memcpy(argtypes, funcform->proargtypes.values,
+                  funcform->pronargs * sizeof(Oid));
+       for (i = 0; i < funcform->pronargs; i++)
+       {
+               if (argtypes[i] == ANYARRAYOID ||
+                       argtypes[i] == ANYELEMENTOID)
+               {
+                       polymorphic = true;
+                       argtypes[i] = exprType((Node *) list_nth(args, i));
+               }
+       }
+
+       if (funcform->prorettype == ANYARRAYOID ||
+               funcform->prorettype == ANYELEMENTOID)
+               polymorphic = true;
+
        /* Fetch and parse the function body */
        tmp = SysCacheGetAttr(PROCOID,
                                                  func_tuple,
                                                  Anum_pg_proc_prosrc,
                                                  &isNull);
        if (isNull)
-               elog(ERROR, "inline_function: null prosrc for procedure %u",
-                        funcid);
+               elog(ERROR, "null prosrc for function %u", funcid);
        src = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
        /*
-        * We just do parsing and parse analysis, not rewriting, because
-        * rewriting will not affect table-free-SELECT-only queries, which is all
-        * that we care about.  Also, we can punt as soon as we detect more than
-        * one command in the function body.
+        * We just do parsing and parse analysis, not rewriting, because rewriting
+        * will not affect table-free-SELECT-only queries, which is all that we
+        * care about.  Also, we can punt as soon as we detect more than one
+        * command in the function body.
         */
        raw_parsetree_list = pg_parse_query(src);
-       if (length(raw_parsetree_list) != 1)
+       if (list_length(raw_parsetree_list) != 1)
                goto fail;
 
-       querytree_list = parse_analyze(lfirst(raw_parsetree_list),
-                                                                  funcform->proargtypes,
-                                                                  funcform->pronargs);
+       querytree_list = parse_analyze(linitial(raw_parsetree_list), src,
+                                                                  argtypes, funcform->pronargs);
 
-       if (length(querytree_list) != 1)
+       if (list_length(querytree_list) != 1)
                goto fail;
 
-       querytree = (Query *) lfirst(querytree_list);
+       querytree = (Query *) linitial(querytree_list);
 
        /*
         * The single command must be a simple "SELECT expression".
         */
        if (!IsA(querytree, Query) ||
                querytree->commandType != CMD_SELECT ||
-               querytree->resultRelation != 0 ||
                querytree->into ||
                querytree->hasAggs ||
                querytree->hasSubLinks ||
@@ -1783,19 +2789,30 @@ inline_function(Oid funcid, Oid result_type, List *args,
                querytree->limitOffset ||
                querytree->limitCount ||
                querytree->setOperations ||
-               length(querytree->targetList) != 1)
+               list_length(querytree->targetList) != 1)
                goto fail;
 
-       newexpr = (Node *) ((TargetEntry *) lfirst(querytree->targetList))->expr;
+       newexpr = (Node *) ((TargetEntry *) linitial(querytree->targetList))->expr;
+
+       /*
+        * If the function has any arguments declared as polymorphic types, then
+        * it wasn't type-checked at definition time; must do so now. (This will
+        * raise an error if wrong, but that's okay since the function would fail
+        * at runtime anyway.  Note we do not try this until we have verified that
+        * no rewriting was needed; that's probably not important, but let's be
+        * careful.)
+        */
+       if (polymorphic)
+               (void) check_sql_fn_retval(funcid, result_type, querytree_list, NULL);
 
        /*
-        * Additional validity checks on the expression.  It mustn't return a
-        * set, and it mustn't be more volatile than the surrounding function
-        * (this is to avoid breaking hacks that involve pretending a function
-        * is immutable when it really ain't).  If the surrounding function is
-        * declared strict, then the expression must contain only strict constructs
-        * and must use all of the function parameters (this is overkill, but
-        * an exact analysis is hard).
+        * Additional validity checks on the expression.  It mustn't return a set,
+        * and it mustn't be more volatile than the surrounding function (this is
+        * to avoid breaking hacks that involve pretending a function is immutable
+        * when it really ain't).  If the surrounding function is declared strict,
+        * then the expression must contain only strict constructs and must use
+        * all of the function parameters (this is overkill, but an exact analysis
+        * is hard).
         */
        if (expression_returns_set(newexpr))
                goto fail;
@@ -1804,7 +2821,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
                contain_mutable_functions(newexpr))
                goto fail;
        else if (funcform->provolatile == PROVOLATILE_STABLE &&
-               contain_volatile_functions(newexpr))
+                        contain_volatile_functions(newexpr))
                goto fail;
 
        if (funcform->proisstrict &&
@@ -1812,12 +2829,12 @@ inline_function(Oid funcid, Oid result_type, List *args,
                goto fail;
 
        /*
-        * We may be able to do it; there are still checks on parameter usage
-        * to make, but those are most easily done in combination with the
-        * actual substitution of the inputs.  So start building expression
-        * with inputs substituted.
+        * We may be able to do it; there are still checks on parameter usage to
+        * make, but those are most easily done in combination with the actual
+        * substitution of the inputs.  So start building expression with inputs
+        * substituted.
         */
-       usecounts = (int *) palloc0((funcform->pronargs + 1) * sizeof(int));
+       usecounts = (int *) palloc0(funcform->pronargs * sizeof(int));
        newexpr = substitute_actual_parameters(newexpr, funcform->pronargs,
                                                                                   args, usecounts);
 
@@ -1825,7 +2842,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
        i = 0;
        foreach(arg, args)
        {
-               Node   *param = lfirst(arg);
+               Node       *param = lfirst(arg);
 
                if (usecounts[i] == 0)
                {
@@ -1835,9 +2852,27 @@ inline_function(Oid funcid, Oid result_type, List *args,
                }
                else if (usecounts[i] != 1)
                {
-                       /* Param used multiple times: uncool if volatile or expensive */
-                       if (contain_volatile_functions(param) ||
-                               contain_subplans(param))
+                       /* Param used multiple times: uncool if expensive or volatile */
+                       QualCost        eval_cost;
+
+                       /*
+                        * We define "expensive" as "contains any subplan or more than 10
+                        * operators".  Note that the subplan search has to be done
+                        * explicitly, since cost_qual_eval() will barf on unplanned
+                        * subselects.
+                        */
+                       if (contain_subplans(param))
+                               goto fail;
+                       cost_qual_eval(&eval_cost, list_make1(param));
+                       if (eval_cost.startup + eval_cost.per_tuple >
+                               10 * cpu_operator_cost)
+                               goto fail;
+
+                       /*
+                        * Check volatility last since this is more expensive than the
+                        * above tests
+                        */
+                       if (contain_volatile_functions(param))
                                goto fail;
                }
                i++;
@@ -1854,11 +2889,14 @@ inline_function(Oid funcid, Oid result_type, List *args,
        MemoryContextDelete(mycxt);
 
        /*
-        * Recursively try to simplify the modified expression.  Here we must
-        * add the current function to the context list of active functions.
+        * 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));
+       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;
 
        return (Expr *) newexpr;
 
@@ -1866,6 +2904,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
 fail:
        MemoryContextSwitchTo(oldcxt);
        MemoryContextDelete(mycxt);
+       error_context_stack = sqlerrcontext.previous;
 
        return NULL;
 }
@@ -1879,7 +2918,7 @@ substitute_actual_parameters(Node *expr, int nargs, List *args,
 {
        substitute_actual_parameters_context context;
 
-       context.nargs = nargs;
+       context.nargs = nargs;
        context.args = args;
        context.usecounts = usecounts;
 
@@ -1888,7 +2927,7 @@ substitute_actual_parameters(Node *expr, int nargs, List *args,
 
 static Node *
 substitute_actual_parameters_mutator(Node *node,
-                                                                        substitute_actual_parameters_context *context)
+                                                          substitute_actual_parameters_context *context)
 {
        if (node == NULL)
                return NULL;
@@ -1896,22 +2935,54 @@ substitute_actual_parameters_mutator(Node *node,
        {
                Param      *param = (Param *) node;
 
-               if (param->paramkind != PARAM_NUM)
-                       elog(ERROR, "substitute_actual_parameters_mutator: unexpected paramkind");
+               if (param->paramkind != PARAM_EXTERN)
+                       elog(ERROR, "unexpected paramkind: %d", (int) param->paramkind);
                if (param->paramid <= 0 || param->paramid > context->nargs)
-                       elog(ERROR, "substitute_actual_parameters_mutator: unexpected paramid");
+                       elog(ERROR, "invalid paramid: %d", param->paramid);
 
                /* Count usage of parameter */
                context->usecounts[param->paramid - 1]++;
 
                /* Select the appropriate actual arg and replace the Param with it */
                /* We don't need to copy at this time (it'll get done later) */
-               return nth(param->paramid - 1, context->args);
+               return list_nth(context->args, param->paramid - 1);
        }
        return expression_tree_mutator(node, substitute_actual_parameters_mutator,
                                                                   (void *) context);
 }
 
+/*
+ * error context callback to let us supply a call-stack traceback
+ */
+static void
+sql_inline_error_callback(void *arg)
+{
+       HeapTuple       func_tuple = (HeapTuple) arg;
+       Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+       int                     syntaxerrposition;
+
+       /* If it's a syntax error, convert to internal syntax error report */
+       syntaxerrposition = geterrposition();
+       if (syntaxerrposition > 0)
+       {
+               bool            isnull;
+               Datum           tmp;
+               char       *prosrc;
+
+               tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc,
+                                                         &isnull);
+               if (isnull)
+                       elog(ERROR, "null prosrc");
+               prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+               errposition(0);
+               internalerrposition(syntaxerrposition);
+               internalerrquery(prosrc);
+       }
+
+       errcontext("SQL function \"%s\" during inlining",
+                          NameStr(funcform->proname));
+}
+
 /*
  * evaluate_expr: pre-evaluate a constant expression
  *
@@ -1945,10 +3016,10 @@ evaluate_expr(Expr *expr, Oid result_type)
        /*
         * And evaluate it.
         *
-        * It is OK to use a default econtext because none of the
-        * ExecEvalExpr() code used in this situation will use econtext.  That
-        * might seem fortuitous, but it's not so unreasonable --- a constant
-        * expression does not depend on context, by definition, n'est ce pas?
+        * It is OK to use a default econtext because none of the ExecEvalExpr()
+        * code used in this situation will use econtext.  That might seem
+        * fortuitous, but it's not so unreasonable --- a constant expression does
+        * not depend on context, by definition, n'est ce pas?
         */
        const_val = ExecEvalExprSwitchContext(exprstate,
                                                                                  GetPerTupleExprContext(estate),
@@ -2032,13 +3103,12 @@ evaluate_expr(Expr *expr, Oid result_type)
  * stage.  In particular, it handles List nodes since a cnf-ified qual clause
  * will have List structure at the top level, and it handles TargetEntry nodes
  * so that a scan of a target list can be handled without additional code.
- * (But only the "expr" part of a TargetEntry is examined, unless the walker
- * chooses to process TargetEntry nodes specially.)  Also, RangeTblRef,
- * FromExpr, JoinExpr, and SetOperationStmt nodes are handled, so that query
- * jointrees and setOperation trees can be processed without additional code.
+ * Also, RangeTblRef, FromExpr, JoinExpr, and SetOperationStmt nodes are
+ * handled, so that query jointrees and setOperation trees can be processed
+ * without additional code.
  *
- * expression_tree_walker will handle SubLink nodes by recursing normally into
- * the "lefthand" arguments (which are expressions belonging to the outer
+ * expression_tree_walker will handle SubLink nodes by recursing normally
+ * into the "testexpr" subtree (which is an expression belonging to the outer
  * plan).  It will also call the walker on the sub-Query node; however, when
  * expression_tree_walker itself is called on a Query node, it does nothing
  * and returns "false".  The net effect is that unless the walker does
@@ -2062,8 +3132,8 @@ evaluate_expr(Expr *expr, Oid result_type)
  * walker on all the expression subtrees of the given Query node.
  *
  * expression_tree_walker will handle SubPlan nodes by recursing normally
- * into the "exprs" and "args" lists (which are expressions belonging to
- * the outer plan).  It will not touch the completed subplan, however.  Since
+ * into the "testexpr" and the "args" list (which are expressions belonging to
+ * the outer plan).  It will not touch the completed subplan, however. Since
  * there is no link to the original Query, it is not possible to recurse into
  * subselects of an already-planned expression tree.  This is OK for current
  * uses, but may need to be revisited in future.
@@ -2075,11 +3145,11 @@ expression_tree_walker(Node *node,
                                           bool (*walker) (),
                                           void *context)
 {
-       List       *temp;
+       ListCell   *temp;
 
        /*
-        * The walker has already visited the current node, and so we need
-        * only recurse into any sub-nodes it has.
+        * The walker has already visited the current node, and so we need only
+        * recurse into any sub-nodes it has.
         *
         * We assume that the walker is not interested in List nodes per se, so
         * when we expect a List we just recurse directly to self without
@@ -2087,17 +3157,32 @@ expression_tree_walker(Node *node,
         */
        if (node == NULL)
                return false;
+
+       /* Guard against stack overflow due to overly complex expressions */
+       check_stack_depth();
+
        switch (nodeTag(node))
        {
                case T_Var:
                case T_Const:
                case T_Param:
                case T_CoerceToDomainValue:
+               case T_CaseTestExpr:
+               case T_SetToDefault:
                case T_RangeTblRef:
-                       /* primitive node types with no subnodes */
+               case T_OuterJoinInfo:
+                       /* primitive node types with no expression subnodes */
                        break;
                case T_Aggref:
-                       return walker(((Aggref *) node)->target, context);
+                       {
+                               Aggref     *expr = (Aggref *) node;
+
+                               /* recurse directly on List */
+                               if (expression_tree_walker((Node *) expr->args,
+                                                                                  walker, context))
+                                       return true;
+                       }
+                       break;
                case T_ArrayRef:
                        {
                                ArrayRef   *aref = (ArrayRef *) node;
@@ -2143,6 +3228,15 @@ expression_tree_walker(Node *node,
                                        return true;
                        }
                        break;
+               case T_ScalarArrayOpExpr:
+                       {
+                               ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+                               if (expression_tree_walker((Node *) expr->args,
+                                                                                  walker, context))
+                                       return true;
+                       }
+                       break;
                case T_BoolExpr:
                        {
                                BoolExpr   *expr = (BoolExpr *) node;
@@ -2156,23 +3250,22 @@ expression_tree_walker(Node *node,
                        {
                                SubLink    *sublink = (SubLink *) node;
 
-                               if (expression_tree_walker((Node *) sublink->lefthand,
-                                                                                  walker, context))
+                               if (walker(sublink->testexpr, context))
                                        return true;
+
                                /*
-                                * Also invoke the walker on the sublink's Query node, so
-                                * it can recurse into the sub-query if it wants to.
+                                * Also invoke the walker on the sublink's Query node, so it
+                                * can recurse into the sub-query if it wants to.
                                 */
                                return walker(sublink->subselect, context);
                        }
                        break;
                case T_SubPlan:
                        {
-                               SubPlan *subplan = (SubPlan *) node;
+                               SubPlan    *subplan = (SubPlan *) node;
 
-                               /* recurse into the exprs list, but not into the Plan */
-                               if (expression_tree_walker((Node *) subplan->exprs,
-                                                                                  walker, context))
+                               /* recurse into the testexpr, but not into the Plan */
+                               if (walker(subplan->testexpr, context))
                                        return true;
                                /* also examine args list */
                                if (expression_tree_walker((Node *) subplan->args,
@@ -2182,12 +3275,26 @@ expression_tree_walker(Node *node,
                        break;
                case T_FieldSelect:
                        return walker(((FieldSelect *) node)->arg, context);
+               case T_FieldStore:
+                       {
+                               FieldStore *fstore = (FieldStore *) node;
+
+                               if (walker(fstore->arg, context))
+                                       return true;
+                               if (walker(fstore->newvals, context))
+                                       return true;
+                       }
+                       break;
                case T_RelabelType:
                        return walker(((RelabelType *) node)->arg, context);
+               case T_ConvertRowtypeExpr:
+                       return walker(((ConvertRowtypeExpr *) node)->arg, context);
                case T_CaseExpr:
                        {
                                CaseExpr   *caseexpr = (CaseExpr *) node;
 
+                               if (walker(caseexpr->arg, context))
+                                       return true;
                                /* we assume walker doesn't care about CaseWhens, either */
                                foreach(temp, caseexpr->args)
                                {
@@ -2199,17 +3306,39 @@ expression_tree_walker(Node *node,
                                        if (walker(when->result, context))
                                                return true;
                                }
-                               /* caseexpr->arg should be null, but we'll check it anyway */
-                               if (walker(caseexpr->arg, context))
-                                       return true;
                                if (walker(caseexpr->defresult, context))
                                        return true;
                        }
                        break;
                case T_ArrayExpr:
                        return walker(((ArrayExpr *) node)->elements, context);
+               case T_RowExpr:
+                       return walker(((RowExpr *) node)->args, context);
+               case T_RowCompareExpr:
+                       {
+                               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+
+                               if (walker(rcexpr->largs, context))
+                                       return true;
+                               if (walker(rcexpr->rargs, context))
+                                       return true;
+                       }
+                       break;
                case T_CoalesceExpr:
                        return walker(((CoalesceExpr *) node)->args, context);
+               case T_MinMaxExpr:
+                       return walker(((MinMaxExpr *) node)->args, context);
+               case T_XmlExpr:
+                       {
+                               XmlExpr *xexpr = (XmlExpr *) node;
+                               
+                               if (walker(xexpr->named_args, context))
+                                       return true;
+                               /* we assume walker doesn't care about arg_names */
+                               if (walker(xexpr->args, context))
+                                       return true;
+                       }
+                       break;
                case T_NullIfExpr:
                        return walker(((NullIfExpr *) node)->args, context);
                case T_NullTest:
@@ -2275,9 +3404,18 @@ expression_tree_walker(Node *node,
                                        return true;
                        }
                        break;
+               case T_AppendRelInfo:
+                       {
+                               AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+                               if (expression_tree_walker((Node *) appinfo->translated_vars,
+                                                                                  walker, context))
+                                       return true;
+                       }
+                       break;
                default:
-                       elog(ERROR, "expression_tree_walker: Unexpected node type %d",
-                                nodeTag(node));
+                       elog(ERROR, "unrecognized node type: %d",
+                                (int) nodeTag(node));
                        break;
        }
        return false;
@@ -2304,21 +3442,73 @@ query_tree_walker(Query *query,
                                  void *context,
                                  int flags)
 {
-       List       *rt;
-
        Assert(query != NULL && IsA(query, Query));
 
        if (walker((Node *) query->targetList, context))
                return true;
+       if (walker((Node *) query->returningList, context))
+               return true;
        if (walker((Node *) query->jointree, context))
                return true;
        if (walker(query->setOperations, context))
                return true;
        if (walker(query->havingQual, context))
                return true;
-       if (walker(query->in_info_list, context))
+       if (walker(query->limitOffset, context))
+               return true;
+       if (walker(query->limitCount, context))
                return true;
-       foreach(rt, query->rtable)
+       if (range_table_walker(query->rtable, walker, context, flags))
+               return true;
+       if (query->utilityStmt)
+       {
+               /*
+                * Certain utility commands contain general-purpose Querys embedded in
+                * them --- if this is one, invoke the walker on the sub-Query.
+                */
+               if (IsA(query->utilityStmt, CopyStmt))
+               {
+                       if (walker(((CopyStmt *) query->utilityStmt)->query, context))
+                               return true;
+               }
+               if (IsA(query->utilityStmt, DeclareCursorStmt))
+               {
+                       if (walker(((DeclareCursorStmt *) query->utilityStmt)->query, context))
+                               return true;
+               }
+               if (IsA(query->utilityStmt, ExplainStmt))
+               {
+                       if (walker(((ExplainStmt *) query->utilityStmt)->query, context))
+                               return true;
+               }
+               if (IsA(query->utilityStmt, PrepareStmt))
+               {
+                       if (walker(((PrepareStmt *) query->utilityStmt)->query, context))
+                               return true;
+               }
+               if (IsA(query->utilityStmt, ViewStmt))
+               {
+                       if (walker(((ViewStmt *) query->utilityStmt)->query, context))
+                               return true;
+               }
+       }
+       return false;
+}
+
+/*
+ * range_table_walker is just the part of query_tree_walker that scans
+ * a query's rangetable.  This is split out since it can be useful on
+ * its own.
+ */
+bool
+range_table_walker(List *rtable,
+                                  bool (*walker) (),
+                                  void *context,
+                                  int flags)
+{
+       ListCell   *rt;
+
+       foreach(rt, rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
@@ -2329,12 +3519,12 @@ query_tree_walker(Query *query,
                                /* nothing to do */
                                break;
                        case RTE_SUBQUERY:
-                               if (! (flags & QTW_IGNORE_RT_SUBQUERIES))
+                               if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
                                        if (walker(rte->subquery, context))
                                                return true;
                                break;
                        case RTE_JOIN:
-                               if (! (flags & QTW_IGNORE_JOINALIASES))
+                               if (!(flags & QTW_IGNORE_JOINALIASES))
                                        if (walker(rte->joinaliasvars, context))
                                                return true;
                                break;
@@ -2342,6 +3532,10 @@ query_tree_walker(Query *query,
                                if (walker(rte->funcexpr, context))
                                        return true;
                                break;
+                       case RTE_VALUES:
+                               if (walker(rte->values_lists, context))
+                                       return true;
+                               break;
                }
        }
        return false;
@@ -2393,7 +3587,7 @@ query_tree_walker(Query *query,
  * and qualifier clauses during the planning stage.
  *
  * expression_tree_mutator will handle SubLink nodes by recursing normally
- * into the "lefthand" arguments (which are expressions belonging to the outer
+ * into the "testexpr" subtree (which is an expression belonging to the outer
  * plan).  It will also call the mutator on the sub-Query node; however, when
  * expression_tree_mutator itself is called on a Query node, it does nothing
  * and returns the unmodified Query node.  The net effect is that unless the
@@ -2402,8 +3596,8 @@ query_tree_walker(Query *query,
  * SubLink node.  Mutators that want to descend into sub-selects will usually
  * do so by recognizing Query nodes and calling query_tree_mutator (below).
  *
- * expression_tree_mutator will handle a SubPlan node by recursing into
- * the "exprs" and "args" lists (which belong to the outer plan), but it
+ * expression_tree_mutator will handle a SubPlan node by recursing into the
+ * "testexpr" and the "args" list (which belong to the outer plan), but it
  * will simply copy the link to the inner plan, since that's typically what
  * expression tree mutators want.  A mutator that wants to modify the subplan
  * can force appropriate behavior by recognizing SubPlan expression nodes
@@ -2417,8 +3611,8 @@ expression_tree_mutator(Node *node,
                                                void *context)
 {
        /*
-        * The mutator has already decided not to modify the current node, but
-        * we must call the mutator for any sub-nodes.
+        * The mutator has already decided not to modify the current node, but we
+        * must call the mutator for any sub-nodes.
         */
 
 #define FLATCOPY(newnode, node, nodetype)  \
@@ -2435,14 +3629,21 @@ expression_tree_mutator(Node *node,
 
        if (node == NULL)
                return NULL;
+
+       /* Guard against stack overflow due to overly complex expressions */
+       check_stack_depth();
+
        switch (nodeTag(node))
        {
                case T_Var:
                case T_Const:
                case T_Param:
                case T_CoerceToDomainValue:
+               case T_CaseTestExpr:
+               case T_SetToDefault:
                case T_RangeTblRef:
-                       /* primitive node types with no subnodes */
+               case T_OuterJoinInfo:
+                       /* primitive node types with no expression subnodes */
                        return (Node *) copyObject(node);
                case T_Aggref:
                        {
@@ -2450,7 +3651,7 @@ expression_tree_mutator(Node *node,
                                Aggref     *newnode;
 
                                FLATCOPY(newnode, aggref, Aggref);
-                               MUTATE(newnode->target, aggref->target, Expr *);
+                               MUTATE(newnode->args, aggref->args, List *);
                                return (Node *) newnode;
                        }
                        break;
@@ -2493,14 +3694,24 @@ expression_tree_mutator(Node *node,
                        break;
                case T_DistinctExpr:
                        {
-                               DistinctExpr   *expr = (DistinctExpr *) node;
-                               DistinctExpr   *newnode;
+                               DistinctExpr *expr = (DistinctExpr *) node;
+                               DistinctExpr *newnode;
 
                                FLATCOPY(newnode, expr, DistinctExpr);
                                MUTATE(newnode->args, expr->args, List *);
                                return (Node *) newnode;
                        }
                        break;
+               case T_ScalarArrayOpExpr:
+                       {
+                               ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+                               ScalarArrayOpExpr *newnode;
+
+                               FLATCOPY(newnode, expr, ScalarArrayOpExpr);
+                               MUTATE(newnode->args, expr->args, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_BoolExpr:
                        {
                                BoolExpr   *expr = (BoolExpr *) node;
@@ -2517,10 +3728,11 @@ expression_tree_mutator(Node *node,
                                SubLink    *newnode;
 
                                FLATCOPY(newnode, sublink, SubLink);
-                               MUTATE(newnode->lefthand, sublink->lefthand, List *);
+                               MUTATE(newnode->testexpr, sublink->testexpr, Node *);
+
                                /*
-                                * Also invoke the mutator on the sublink's Query node, so
-                                * it can recurse into the sub-query if it wants to.
+                                * Also invoke the mutator on the sublink's Query node, so it
+                                * can recurse into the sub-query if it wants to.
                                 */
                                MUTATE(newnode->subselect, sublink->subselect, Node *);
                                return (Node *) newnode;
@@ -2528,12 +3740,12 @@ expression_tree_mutator(Node *node,
                        break;
                case T_SubPlan:
                        {
-                               SubPlan    *subplan = (SubPlan *) node;
-                               SubPlan    *newnode;
+                               SubPlan    *subplan = (SubPlan *) node;
+                               SubPlan    *newnode;
 
                                FLATCOPY(newnode, subplan, SubPlan);
-                               /* transform exprs list */
-                               MUTATE(newnode->exprs, subplan->exprs, List *);
+                               /* transform testexpr */
+                               MUTATE(newnode->testexpr, subplan->testexpr, Node *);
                                /* transform args list (params to be passed to subplan) */
                                MUTATE(newnode->args, subplan->args, List *);
                                /* but not the sub-Plan itself, which is referenced as-is */
@@ -2550,6 +3762,18 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_FieldStore:
+                       {
+                               FieldStore *fstore = (FieldStore *) node;
+                               FieldStore *newnode;
+
+                               FLATCOPY(newnode, fstore, FieldStore);
+                               MUTATE(newnode->arg, fstore->arg, Expr *);
+                               MUTATE(newnode->newvals, fstore->newvals, List *);
+                               newnode->fieldnums = list_copy(fstore->fieldnums);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_RelabelType:
                        {
                                RelabelType *relabel = (RelabelType *) node;
@@ -2560,15 +3784,24 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_ConvertRowtypeExpr:
+                       {
+                               ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
+                               ConvertRowtypeExpr *newnode;
+
+                               FLATCOPY(newnode, convexpr, ConvertRowtypeExpr);
+                               MUTATE(newnode->arg, convexpr->arg, Expr *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_CaseExpr:
                        {
                                CaseExpr   *caseexpr = (CaseExpr *) node;
                                CaseExpr   *newnode;
 
                                FLATCOPY(newnode, caseexpr, CaseExpr);
-                               MUTATE(newnode->args, caseexpr->args, List *);
-                               /* caseexpr->arg should be null, but we'll check it anyway */
                                MUTATE(newnode->arg, caseexpr->arg, Expr *);
+                               MUTATE(newnode->args, caseexpr->args, List *);
                                MUTATE(newnode->defresult, caseexpr->defresult, Expr *);
                                return (Node *) newnode;
                        }
@@ -2586,14 +3819,35 @@ expression_tree_mutator(Node *node,
                        break;
                case T_ArrayExpr:
                        {
-                               ArrayExpr *arrayexpr = (ArrayExpr *) node;
-                               ArrayExpr *newnode;
+                               ArrayExpr  *arrayexpr = (ArrayExpr *) node;
+                               ArrayExpr  *newnode;
 
                                FLATCOPY(newnode, arrayexpr, ArrayExpr);
                                MUTATE(newnode->elements, arrayexpr->elements, List *);
                                return (Node *) newnode;
                        }
                        break;
+               case T_RowExpr:
+                       {
+                               RowExpr    *rowexpr = (RowExpr *) node;
+                               RowExpr    *newnode;
+
+                               FLATCOPY(newnode, rowexpr, RowExpr);
+                               MUTATE(newnode->args, rowexpr->args, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
+               case T_RowCompareExpr:
+                       {
+                               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+                               RowCompareExpr *newnode;
+
+                               FLATCOPY(newnode, rcexpr, RowCompareExpr);
+                               MUTATE(newnode->largs, rcexpr->largs, List *);
+                               MUTATE(newnode->rargs, rcexpr->rargs, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_CoalesceExpr:
                        {
                                CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
@@ -2604,10 +3858,32 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_MinMaxExpr:
+                       {
+                               MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+                               MinMaxExpr *newnode;
+
+                               FLATCOPY(newnode, minmaxexpr, MinMaxExpr);
+                               MUTATE(newnode->args, minmaxexpr->args, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
+               case T_XmlExpr:
+                       {
+                               XmlExpr *xexpr = (XmlExpr *) node;
+                               XmlExpr *newnode;
+
+                               FLATCOPY(newnode, xexpr, XmlExpr);
+                               MUTATE(newnode->named_args, xexpr->named_args, List *);
+                               /* assume mutator does not care about arg_names */
+                               MUTATE(newnode->args, xexpr->args, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_NullIfExpr:
                        {
-                               NullIfExpr   *expr = (NullIfExpr *) node;
-                               NullIfExpr   *newnode;
+                               NullIfExpr *expr = (NullIfExpr *) node;
+                               NullIfExpr *newnode;
 
                                FLATCOPY(newnode, expr, NullIfExpr);
                                MUTATE(newnode->args, expr->args, List *);
@@ -2646,10 +3922,6 @@ expression_tree_mutator(Node *node,
                        break;
                case T_TargetEntry:
                        {
-                               /*
-                                * We mutate the expression, but not the resdom, by
-                                * default.
-                                */
                                TargetEntry *targetentry = (TargetEntry *) node;
                                TargetEntry *newnode;
 
@@ -2664,14 +3936,14 @@ expression_tree_mutator(Node *node,
                case T_List:
                        {
                                /*
-                                * We assume the mutator isn't interested in the list
-                                * nodes per se, so just invoke it on each list element.
-                                * NOTE: this would fail badly on a list with integer
-                                * elements!
+                                * We assume the mutator isn't interested in the list nodes
+                                * per se, so just invoke it on each list element. NOTE: this
+                                * would fail badly on a list with integer elements!
                                 */
-                               List       *resultlist = NIL;
-                               List       *temp;
+                               List       *resultlist;
+                               ListCell   *temp;
 
+                               resultlist = NIL;
                                foreach(temp, (List *) node)
                                {
                                        resultlist = lappend(resultlist,
@@ -2726,9 +3998,19 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_AppendRelInfo:
+                       {
+                               AppendRelInfo *appinfo = (AppendRelInfo *) node;
+                               AppendRelInfo *newnode;
+
+                               FLATCOPY(newnode, appinfo, AppendRelInfo);
+                               MUTATE(newnode->translated_vars, appinfo->translated_vars, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
                default:
-                       elog(ERROR, "expression_tree_mutator: Unexpected node type %d",
-                                nodeTag(node));
+                       elog(ERROR, "unrecognized node type: %d",
+                                (int) nodeTag(node));
                        break;
        }
        /* can't get here, but keep compiler happy */
@@ -2752,7 +4034,7 @@ expression_tree_mutator(Node *node,
  * indicated items.  (More flag bits may be added as needed.)
  *
  * Normally the Query node itself is copied, but some callers want it to be
- * modified in-place; they must pass QTW_DONT_COPY_QUERY in flags.  All
+ * modified in-place; they must pass QTW_DONT_COPY_QUERY in flags.     All
  * modified substructure is safely copied in any case.
  */
 Query *
@@ -2761,62 +4043,85 @@ query_tree_mutator(Query *query,
                                   void *context,
                                   int flags)
 {
-       List       *newrt = NIL;
-       List       *rt;
-
        Assert(query != NULL && IsA(query, Query));
 
-       if (! (flags & QTW_DONT_COPY_QUERY))
+       if (!(flags & QTW_DONT_COPY_QUERY))
        {
-               Query  *newquery;
+               Query      *newquery;
 
                FLATCOPY(newquery, query, Query);
                query = newquery;
        }
 
        MUTATE(query->targetList, query->targetList, List *);
+       MUTATE(query->returningList, query->returningList, List *);
        MUTATE(query->jointree, query->jointree, FromExpr *);
        MUTATE(query->setOperations, query->setOperations, Node *);
        MUTATE(query->havingQual, query->havingQual, Node *);
-       MUTATE(query->in_info_list, query->in_info_list, List *);
-       foreach(rt, query->rtable)
+       MUTATE(query->limitOffset, query->limitOffset, Node *);
+       MUTATE(query->limitCount, query->limitCount, Node *);
+       query->rtable = range_table_mutator(query->rtable,
+                                                                               mutator, context, flags);
+       return query;
+}
+
+/*
+ * range_table_mutator is just the part of query_tree_mutator that processes
+ * a query's rangetable.  This is split out since it can be useful on
+ * its own.
+ */
+List *
+range_table_mutator(List *rtable,
+                                       Node *(*mutator) (),
+                                       void *context,
+                                       int flags)
+{
+       List       *newrt = NIL;
+       ListCell   *rt;
+
+       foreach(rt, rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
                RangeTblEntry *newrte;
 
+               FLATCOPY(newrte, rte, RangeTblEntry);
                switch (rte->rtekind)
                {
                        case RTE_RELATION:
                        case RTE_SPECIAL:
-                               /* nothing to do, don't bother to make a copy */
+                               /* we don't bother to copy eref, aliases, etc; OK? */
                                break;
                        case RTE_SUBQUERY:
-                               if (! (flags & QTW_IGNORE_RT_SUBQUERIES))
+                               if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
                                {
-                                       FLATCOPY(newrte, rte, RangeTblEntry);
                                        CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
                                        MUTATE(newrte->subquery, newrte->subquery, Query *);
-                                       rte = newrte;
+                               }
+                               else
+                               {
+                                       /* else, copy RT subqueries as-is */
+                                       newrte->subquery = copyObject(rte->subquery);
                                }
                                break;
                        case RTE_JOIN:
-                               if (! (flags & QTW_IGNORE_JOINALIASES))
-                               {
-                                       FLATCOPY(newrte, rte, RangeTblEntry);
+                               if (!(flags & QTW_IGNORE_JOINALIASES))
                                        MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
-                                       rte = newrte;
+                               else
+                               {
+                                       /* else, copy join aliases as-is */
+                                       newrte->joinaliasvars = copyObject(rte->joinaliasvars);
                                }
                                break;
                        case RTE_FUNCTION:
-                               FLATCOPY(newrte, rte, RangeTblEntry);
                                MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
-                               rte = newrte;
+                               break;
+                       case RTE_VALUES:
+                               MUTATE(newrte->values_lists, rte->values_lists, List *);
                                break;
                }
-               newrt = lappend(newrt, rte);
+               newrt = lappend(newrt, newrte);
        }
-       query->rtable = newrt;
-       return query;
+       return newrt;
 }
 
 /*