]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/util/clauses.c
Change the planner-to-executor API so that the planner tells the executor
[postgresql] / src / backend / optimizer / util / clauses.c
index f941375127c4cd4397019b9c064193b375a5ae66..4858f985cffe2eb0f1f061a866aba3d7a996c37a 100644 (file)
@@ -3,12 +3,12 @@
  * clauses.c
  *       routines to manipulate qualification clauses
  *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.178 2004/08/17 18:47:08 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.229 2007/01/10 18:06:04 tgl 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 "optimizer/clauses.h"
@@ -32,6 +35,7 @@
 #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"
@@ -46,6 +50,7 @@
 typedef struct
 {
        List       *active_fns;
+       Node       *case_val;
        bool            estimate;
 } eval_const_expressions_context;
 
@@ -57,32 +62,37 @@ 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 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);
+                                                          eval_const_expressions_context *context);
 static List *simplify_or_arguments(List *args,
-                                                                  bool *haveNull, bool *forceTrue);
+                                         eval_const_expressions_context *context,
+                                         bool *haveNull, bool *forceTrue);
 static List *simplify_and_arguments(List *args,
-                                                                       bool *haveNull, bool *forceFalse);
+                                          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,
-                                                          eval_const_expressions_context *context);
+                                 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,
-                                                        eval_const_expressions_context *context);
+                               HeapTuple func_tuple,
+                               eval_const_expressions_context *context);
 static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
                                                         int *usecounts);
 static Node *substitute_actual_parameters_mutator(Node *node,
-                                                 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);
 
@@ -299,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 */
@@ -348,82 +358,130 @@ contain_agg_clause_walker(Node *node, void *context)
        if (IsA(node, Aggref))
        {
                Assert(((Aggref *) node)->agglevelsup == 0);
-               return true;                    /* abort the tree traversal and return
-                                                                * true */
+               return true;                    /* abort the tree traversal and return true */
        }
        Assert(!IsA(node, SubLink));
        return expression_tree_walker(node, contain_agg_clause_walker, context);
 }
 
 /*
- * contain_distinct_agg_clause
- *       Recursively search for DISTINCT Aggref nodes within a clause.
+ * count_agg_clauses
+ *       Recursively count the Aggref nodes in an expression tree.
  *
- *       Returns true if any DISTINCT aggregate found.
+ *       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.
  */
-bool
-contain_distinct_agg_clause(Node *clause)
+void
+count_agg_clauses(Node *clause, AggClauseCounts *counts)
 {
-       return contain_distinct_agg_clause_walker(clause, NULL);
+       /* no setup needed */
+       count_agg_clauses_walker(clause, counts);
 }
 
 static bool
-contain_distinct_agg_clause_walker(Node *node, void *context)
+count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
 {
        if (node == NULL)
                return false;
        if (IsA(node, Aggref))
        {
-               Assert(((Aggref *) node)->agglevelsup == 0);
-               if (((Aggref *) node)->aggdistinct)
-                       return true;            /* abort the tree traversal and return
-                                                                * true */
-       }
-       Assert(!IsA(node, SubLink));
-       return expression_tree_walker(node, contain_distinct_agg_clause_walker, context);
-}
+               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));
+               }
 
-/*
- * count_agg_clause
- *       Recursively count the Aggref nodes in an expression tree.
- *
- *       Note: this also checks for nested aggregates, which are an error.
- *
- * 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)
-{
-       int                     result = 0;
+               /* 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);
+               }
 
-       count_agg_clause_walker(clause, &result);
-       return result;
-}
+               /*
+                * 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;
 
-static bool
-count_agg_clause_walker(Node *node, int *count)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Aggref))
-       {
-               Assert(((Aggref *) node)->agglevelsup == 0);
-               (*count)++;
+                       /*
+                        * 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))
+               if (contain_agg_clause((Node *) aggref->args))
                        ereport(ERROR,
                                        (errcode(ERRCODE_GROUPING_ERROR),
-                                 errmsg("aggregate function calls may not be nested")));
+                                        errmsg("aggregate function calls may not be nested")));
 
                /*
                 * Having checked that, we need not recurse into the argument.
@@ -431,8 +489,8 @@ count_agg_clause_walker(Node *node, int *count)
                return false;
        }
        Assert(!IsA(node, SubLink));
-       return expression_tree_walker(node, count_agg_clause_walker,
-                                                                 (void *) count);
+       return expression_tree_walker(node, count_agg_clauses_walker,
+                                                                 (void *) counts);
 }
 
 
@@ -493,8 +551,14 @@ expression_returns_set_walker(Node *node, void *context)
                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;
 
@@ -530,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);
 }
 
@@ -603,12 +666,12 @@ contain_mutable_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, RowCompareExpr))
        {
-               SubLink    *sublink = (SubLink *) node;
+               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
                ListCell   *opid;
 
-               foreach(opid, sublink->operOids)
+               foreach(opid, rcexpr->opnos)
                {
                        if (op_volatile(lfirst_oid(opid)) != PROVOLATILE_IMMUTABLE)
                                return true;
@@ -686,12 +749,13 @@ contain_volatile_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, RowCompareExpr))
        {
-               SubLink    *sublink = (SubLink *) node;
+               /* 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(lfirst_oid(opid)) == PROVOLATILE_VOLATILE)
                                return true;
@@ -717,7 +781,7 @@ contain_volatile_functions_walker(Node *node, void *context)
  * 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.
+ * inputs is NULL.     If we return false, then the proof succeeded.
  */
 bool
 contain_nonstrict_functions(Node *clause)
@@ -737,7 +801,7 @@ contain_nonstrict_functions_walker(Node *node, void *context)
        }
        if (IsA(node, ArrayRef))
        {
-               /* array assignment is nonstrict */
+               /* array assignment is nonstrict, but subscripting is strict */
                if (((ArrayRef *) node)->refassgnexpr != NULL)
                        return true;
                /* else fall through to check args */
@@ -765,8 +829,11 @@ contain_nonstrict_functions_walker(Node *node, void *context)
        }
        if (IsA(node, ScalarArrayOpExpr))
        {
-               /* inherently non-strict, consider null scalar and empty array */
-               return true;
+               ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+               if (!is_strict_saop(expr, false))
+                       return true;
+               /* else fall through to check args */
        }
        if (IsA(node, BoolExpr))
        {
@@ -795,11 +862,18 @@ contain_nonstrict_functions_walker(Node *node, void *context)
                return true;
        if (IsA(node, CaseWhen))
                return true;
-       /* NB: ArrayExpr might someday be nonstrict */
+       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))
@@ -811,29 +885,203 @@ contain_nonstrict_functions_walker(Node *node, void *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))
+       {
+               Var                *var = (Var *) node;
+
+               if (var->varlevelsup == 0)
+                       result = bms_make_singleton(var->varno);
+       }
+       else if (IsA(node, List))
+       {
+               ListCell   *l;
+
+               foreach(l, (List *) node)
+               {
+                       result = bms_join(result,
+                                                         find_nonnullable_rels_walker(lfirst(l),
+                                                                                                                  top_level));
+               }
+       }
+       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;
+}
+
+
 /*****************************************************************************
  *             Check for "pseudo-constant" clauses
  *****************************************************************************/
 
 /*
  * 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))
@@ -844,7 +1092,7 @@ is_pseudo_constant_clause(Node *clause)
 /*
  * is_pseudo_constant_clause_relids
  *       Same as above, except caller already has available the var membership
- *       of the clause; this lets us avoid the contain_var_clause() scan.
+ *       of the expression; this lets us avoid the contain_var_clause() scan.
  */
 bool
 is_pseudo_constant_clause_relids(Node *clause, Relids relids)
@@ -855,34 +1103,6 @@ is_pseudo_constant_clause_relids(Node *clause, Relids relids)
        return false;
 }
 
-/*
- * 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.
- */
-List *
-pull_constant_clauses(List *quals, List **constantQual)
-{
-       List       *constqual = NIL,
-                          *restqual = NIL;
-       ListCell   *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;
-}
-
 
 /*****************************************************************************
  *             Tests on clauses of queries
@@ -907,13 +1127,12 @@ 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.
@@ -922,24 +1141,24 @@ has_distinct_on_clause(Query *query)
        {
                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 (targetIsInSortList(tle, InvalidOid, 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))
+                       if (targetIsInSortList(tle, InvalidOid, query->sortClause))
                                return true;    /* sorted, non-distinct junk */
                        /* unsorted junk is okay, keep looking */
                }
@@ -987,12 +1206,12 @@ 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;
@@ -1020,6 +1239,112 @@ CommuteClause(OpExpr *clause)
        lsecond(clause->args) = temp;
 }
 
+/*
+ * CommuteRowCompareExpr: commute a RowCompareExpr clause
+ *
+ * XXX the clause is destructively modified!
+ */
+void
+CommuteRowCompareExpr(RowCompareExpr *clause)
+{
+       List       *newops;
+       List       *temp;
+       ListCell   *l;
+
+       /* 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
  *
@@ -1045,11 +1370,13 @@ set_coercionform_dontcare_walker(Node *node, void *context)
                return false;
        if (IsA(node, FuncExpr))
                ((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
-       if (IsA(node, RelabelType))
+       else if (IsA(node, RelabelType))
                ((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
-       if (IsA(node, RowExpr))
+       else if (IsA(node, ConvertRowtypeExpr))
+               ((ConvertRowtypeExpr *) node)->convertformat = COERCE_DONTCARE;
+       else if (IsA(node, RowExpr))
                ((RowExpr *) node)->row_format = COERCE_DONTCARE;
-       if (IsA(node, CoerceToDomain))
+       else if (IsA(node, CoerceToDomain))
                ((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE;
        return expression_tree_walker(node, set_coercionform_dontcare_walker,
                                                                  context);
@@ -1073,12 +1400,19 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
                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;
 }
 
@@ -1103,6 +1437,9 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
  *
  * 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 *
@@ -1111,11 +1448,12 @@ 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
@@ -1123,11 +1461,13 @@ eval_const_expressions(Node *node)
  * eval_const_expressions(): we will perform constant reductions that are
  * not necessarily 100% safe, but are reasonable for estimation purposes.
  *
- * Currently the only such transform is to substitute values for Params,
- * when a bound Param value has been made available by the caller of planner().
- * In future we might consider other things, such as reducing now() to current
- * time.  (XXX seems like there could be a lot of scope for ideas here...
- * but we might need more volatility classifications ...)
+ * 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)
@@ -1135,6 +1475,7 @@ 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);
 }
@@ -1149,37 +1490,41 @@ eval_const_expressions_mutator(Node *node,
        {
                Param      *param = (Param *) node;
 
-               /* OK to try to substitute value? */
-               if (context->estimate && param->paramkind != PARAM_EXEC &&
-                       PlannerBoundParamList != NULL)
+               /* 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)
                {
-                       ParamListInfo paramInfo;
+                       ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
 
-                       /* Search to see if we've been given a value for this Param */
-                       paramInfo = lookupParam(PlannerBoundParamList,
-                                                                       param->paramkind,
-                                                                       param->paramname,
-                                                                       param->paramid,
-                                                                       true);
-                       if (paramInfo)
+                       if (OidIsValid(prm->ptype))
                        {
-                               /*
-                                * Found it, so return a Const representing the param value.
-                                * Note that we don't copy pass-by-ref datatypes, so the
-                                * Const will only be valid as long as the bound parameter
-                                * list exists. This is okay for intended uses of
-                                * estimate_expression_value().
-                                */
-                               int16           typLen;
-                               bool            typByVal;
-
-                               Assert(paramInfo->ptype == param->paramtype);
-                               get_typlenbyval(param->paramtype, &typLen, &typByVal);
-                               return (Node *) makeConst(param->paramtype,
-                                                                                 (int) typLen,
-                                                                                 paramInfo->value,
-                                                                                 paramInfo->isnull,
-                                                                                 typByVal);
+                               /* 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) */
@@ -1194,16 +1539,16 @@ eval_const_expressions_mutator(Node *node,
 
                /*
                 * 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,
+                                                                                         eval_const_expressions_mutator,
                                                                                                (void *) context);
 
                /*
-                * Code for op/func reduction is pretty bulky, so split it out as
-                * 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, context);
@@ -1212,8 +1557,8 @@ eval_const_expressions_mutator(Node *node,
 
                /*
                 * The expression cannot be simplified any further, so build and
-                * return a replacement FuncExpr node using the
-                * possibly-simplified arguments.
+                * return a replacement FuncExpr node using the possibly-simplified
+                * arguments.
                 */
                newexpr = makeNode(FuncExpr);
                newexpr->funcid = expr->funcid;
@@ -1231,29 +1576,40 @@ eval_const_expressions_mutator(Node *node,
                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,
+                                                                                         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
-                * 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, 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
@@ -1279,18 +1635,17 @@ eval_const_expressions_mutator(Node *node,
                DistinctExpr *newexpr;
 
                /*
-                * 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.
+                * 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.
                 */
                args = (List *) expression_tree_mutator((Node *) expr->args,
-                                                                                 eval_const_expressions_mutator,
+                                                                                         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.
+                * different results for NULL input than the underlying operator does.
                 */
                foreach(arg, args)
                {
@@ -1318,15 +1673,14 @@ eval_const_expressions_mutator(Node *node,
                        /* (NOT okay to try to inline it, though!) */
 
                        /*
-                        * 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((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, context);
@@ -1361,16 +1715,6 @@ eval_const_expressions_mutator(Node *node,
        if (IsA(node, BoolExpr))
        {
                BoolExpr   *expr = (BoolExpr *) node;
-               List       *args;
-
-               /*
-                * 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 *) context);
 
                switch (expr->boolop)
                {
@@ -1380,7 +1724,7 @@ eval_const_expressions_mutator(Node *node,
                                        bool            haveNull = false;
                                        bool            forceTrue = false;
 
-                                       newargs = simplify_or_arguments(args,
+                                       newargs = simplify_or_arguments(expr->args, context,
                                                                                                        &haveNull, &forceTrue);
                                        if (forceTrue)
                                                return makeBoolConst(true, false);
@@ -1401,7 +1745,7 @@ eval_const_expressions_mutator(Node *node,
                                        bool            haveNull = false;
                                        bool            forceFalse = false;
 
-                                       newargs = simplify_and_arguments(args,
+                                       newargs = simplify_and_arguments(expr->args, context,
                                                                                                         &haveNull, &forceFalse);
                                        if (forceFalse)
                                                return makeBoolConst(false, false);
@@ -1417,25 +1761,31 @@ eval_const_expressions_mutator(Node *node,
                                        return (Node *) make_andclause(newargs);
                                }
                        case NOT_EXPR:
-                               Assert(list_length(args) == 1);
-                               if (IsA(linitial(args), Const))
-                               {
-                                       Const *const_input = (Const *) linitial(args);
-
-                                       /* 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((Node *) linitial(args)))
                                {
-                                       /* Cancel NOT/NOT */
-                                       return (Node *) get_notclausearg((Expr *) linitial(args));
+                                       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((Expr *) linitial(args));
                        default:
                                elog(ERROR, "unrecognized boolop: %d",
                                         (int) expr->boolop);
@@ -1447,17 +1797,17 @@ eval_const_expressions_mutator(Node *node,
                /*
                 * Return a SubPlan unchanged --- too late to do anything with it.
                 *
-                * XXX should we ereport() 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;
@@ -1466,8 +1816,8 @@ eval_const_expressions_mutator(Node *node,
                                                                                         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;
@@ -1479,10 +1829,9 @@ eval_const_expressions_mutator(Node *node,
                        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;
                }
@@ -1508,71 +1857,98 @@ eval_const_expressions_mutator(Node *node,
                 * 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 and
-                * one or more constant comparison expressions, we could run the
-                * implied comparisons and potentially reduce those arms to constants.
-                * This is not yet implemented, however.  At present, the
-                * CaseTestExpr placeholder will always act as a non-constant node
-                * and prevent the comparison boolean expressions from being reduced
-                * to Const nodes.
+                * 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;
+               Node       *save_case_val;
                Node       *newarg;
                List       *newargs;
-               Node       *defresult;
-               Const      *const_input;
+               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 *) context);
+                       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,
-                                                                                                  context);
+               /* 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 */
@@ -1583,6 +1959,18 @@ eval_const_expressions_mutator(Node *node,
                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;
@@ -1645,6 +2033,10 @@ eval_const_expressions_mutator(Node *node,
                        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;
@@ -1653,110 +2045,295 @@ eval_const_expressions_mutator(Node *node,
        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 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.
+                * 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,
+               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, 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))
+               if (arg && IsA(arg, Const))
                {
-                       RowExpr *rowexpr = (RowExpr *) arg;
+                       Const      *carg = (Const *) arg;
+                       bool            result;
 
-                       if (fselect->fieldnum > 0 &&
-                               fselect->fieldnum <= list_length(rowexpr->args))
+                       switch (btest->booltesttype)
                        {
-                               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;
+                               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);
                }
-               newfselect = makeNode(FieldSelect);
-               newfselect->arg = (Expr *) arg;
-               newfselect->fieldnum = fselect->fieldnum;
-               newfselect->resulttype = fselect->resulttype;
-               newfselect->resulttypmod = fselect->resulttypmod;
-               return (Node *) newfselect;
+
+               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.
+        * 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: scan the arguments of an OR clause
+ * 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.
  *
- * OR arguments are handled as follows:
+ * 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.
- *
- * This is split out as a subroutine so that we can recurse to fold sub-ORs
- * into the upper OR clause, thereby preserving AND/OR flatness.
+ * 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, bool *haveNull, bool *forceTrue)
+simplify_or_arguments(List *args,
+                                         eval_const_expressions_context *context,
+                                         bool *haveNull, bool *forceTrue)
 {
        List       *newargs = NIL;
-       ListCell   *larg;
+       List       *unprocessed_args;
 
-       foreach(larg, 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 *) lfirst(larg);
+               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;
+                       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
@@ -1765,59 +2342,100 @@ simplify_or_arguments(List *args, bool *haveNull, bool *forceTrue)
                                return NIL;
                        }
                        /* otherwise, we can drop the constant-false input */
+                       continue;
                }
-               else if (or_clause(arg))
-               {
-                       newargs = list_concat(newargs,
-                                                                 simplify_or_arguments(((BoolExpr *) arg)->args,
-                                                                                                               haveNull, forceTrue));
-               }
-               else
-               {
-                       newargs = lappend(newargs, arg);
-               }
+
+               /* else emit the simplified arg into the result list */
+               newargs = lappend(newargs, arg);
        }
 
        return newargs;
 }
 
 /*
- * Subroutine for eval_const_expressions: scan the arguments of an AND clause
+ * Subroutine for eval_const_expressions: process arguments of an AND clause
  *
- * AND arguments are handled as follows:
+ * 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.
- *
- * This is split out as a subroutine so that we can recurse to fold sub-ANDs
- * into the upper AND clause, thereby preserving AND/OR flatness.
+ * 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, bool *haveNull, bool *forceFalse)
+simplify_and_arguments(List *args,
+                                          eval_const_expressions_context *context,
+                                          bool *haveNull, bool *forceFalse)
 {
        List       *newargs = NIL;
-       ListCell   *larg;
+       List       *unprocessed_args;
 
-       foreach(larg, args)
+       /* See comments in simplify_or_arguments */
+       unprocessed_args = list_copy(args);
+       while (unprocessed_args)
        {
-               Node   *arg = (Node *) lfirst(larg);
+               Node       *arg = (Node *) linitial(unprocessed_args);
+
+               unprocessed_args = list_delete_first(unprocessed_args);
+
+               /* flatten nested ANDs as per above comment */
+               if (and_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 AND, simplify it */
+               arg = eval_const_expressions_mutator(arg, context);
+
+               /*
+                * 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.
+                */
+               if (and_clause(arg))
+               {
+                       List       *subargs = list_copy(((BoolExpr *) arg)->args);
 
+                       unprocessed_args = list_concat(subargs, unprocessed_args);
+                       continue;
+               }
+
+               /*
+                * OK, we have a const-simplified non-AND argument.  Process it per
+                * comments above.
+                */
                if (IsA(arg, Const))
                {
-                       Const  *const_input = (Const *) arg;
+                       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
@@ -1826,22 +2444,59 @@ simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse)
                                return NIL;
                        }
                        /* otherwise, we can drop the constant-true input */
+                       continue;
                }
-               else if (and_clause(arg))
-               {
-                       newargs = list_concat(newargs,
-                                                                 simplify_and_arguments(((BoolExpr *) arg)->args,
-                                                                                                                haveNull, forceFalse));
-               }
-               else
-               {
-                       newargs = lappend(newargs, arg);
-               }
+
+               /* else emit the simplified arg into the result list */
+               newargs = lappend(newargs, arg);
        }
 
        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;
+}
+
 /*
  * Subroutine for eval_const_expressions: try to simplify a function call
  * (which might originally have been an operator; we don't care)
@@ -1862,12 +2517,12 @@ simplify_function(Oid funcid, Oid result_type, List *args,
        Expr       *newexpr;
 
        /*
-        * We have two strategies for simplification: either execute the
-        * function to deliver a constant result, or expand in-line the body
-        * of the function definition (which only works for simple
-        * SQL-language functions, but that is a common case).  In either case
-        * we need access to the function's pg_proc tuple, so fetch it just
-        * once to use in both attempts.
+        * We have two strategies for simplification: either execute the function
+        * to deliver a constant result, or expand in-line the body of the
+        * function definition (which only works for simple SQL-language
+        * functions, but that is a common case).  In either case we need access
+        * to the function's pg_proc tuple, so fetch it just once to use in both
+        * attempts.
         */
        func_tuple = SearchSysCache(PROCOID,
                                                                ObjectIdGetDatum(funcid),
@@ -1875,7 +2530,8 @@ simplify_function(Oid funcid, Oid result_type, List *args,
        if (!HeapTupleIsValid(func_tuple))
                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,
@@ -1891,14 +2547,16 @@ 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;
@@ -1912,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.
         */
@@ -1924,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;
 
        /*
@@ -1988,7 +2673,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
 {
        Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
        bool            polymorphic = false;
-       Oid                     argtypes[FUNC_MAX_ARGS];
+       Oid                *argtypes;
        char       *src;
        Datum           tmp;
        bool            isNull;
@@ -2004,8 +2689,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
        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 ||
@@ -2021,25 +2706,9 @@ inline_function(Oid funcid, Oid result_type, List *args,
        if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
                return NULL;
 
-       /* Check for polymorphic arguments, and substitute actual arg types */
-       memcpy(argtypes, funcform->proargtypes, FUNC_MAX_ARGS * 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;
-
        /*
-        * Setup error traceback support for ereport().  This is so that we
-        * can finger the function that bad information came from.
+        * 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;
@@ -2047,8 +2716,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
        error_context_stack = &sqlerrcontext;
 
        /*
-        * Make a temporary memory context, so that we don't leak all the
-        * stuff that parsing might create.
+        * Make a temporary memory context, so that we don't leak all the stuff
+        * that parsing might create.
         */
        mycxt = AllocSetContextCreate(CurrentMemoryContext,
                                                                  "inline_function",
@@ -2057,6 +2726,24 @@ 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,
@@ -2067,16 +2754,16 @@ inline_function(Oid funcid, Oid result_type, List *args,
        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 (list_length(raw_parsetree_list) != 1)
                goto fail;
 
-       querytree_list = parse_analyze(linitial(raw_parsetree_list),
+       querytree_list = parse_analyze(linitial(raw_parsetree_list), src,
                                                                   argtypes, funcform->pronargs);
 
        if (list_length(querytree_list) != 1)
@@ -2089,7 +2776,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
         */
        if (!IsA(querytree, Query) ||
                querytree->commandType != CMD_SELECT ||
-               querytree->resultRelation != 0 ||
                querytree->into ||
                querytree->hasAggs ||
                querytree->hasSubLinks ||
@@ -2109,25 +2795,24 @@ inline_function(Oid funcid, Oid result_type, List *args,
        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 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(result_type, get_typtype(result_type),
-                                                                  querytree_list);
+               (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;
@@ -2144,10 +2829,10 @@ 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 * sizeof(int));
        newexpr = substitute_actual_parameters(newexpr, funcform->pronargs,
@@ -2171,8 +2856,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
                        QualCost        eval_cost;
 
                        /*
-                        * We define "expensive" as "contains any subplan or more than
-                        * 10 operators".  Note that the subplan search has to be done
+                        * 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.
                         */
@@ -2194,8 +2879,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
        }
 
        /*
-        * Whew --- we can make the substitution.  Copy the modified
-        * expression out of the temporary memory context, and clean up.
+        * Whew --- we can make the substitution.  Copy the modified expression
+        * out of the temporary memory context, and clean up.
         */
        MemoryContextSwitchTo(oldcxt);
 
@@ -2204,8 +2889,8 @@ 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.
         */
        context->active_fns = lcons_oid(funcid, context->active_fns);
        newexpr = eval_const_expressions_mutator(newexpr, context);
@@ -2242,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;
@@ -2250,8 +2935,8 @@ substitute_actual_parameters_mutator(Node *node,
        {
                Param      *param = (Param *) node;
 
-               if (param->paramkind != PARAM_NUM)
-                       elog(ERROR, "unexpected paramkind: %d", param->paramkind);
+               if (param->paramkind != PARAM_EXTERN)
+                       elog(ERROR, "unexpected paramkind: %d", (int) param->paramkind);
                if (param->paramid <= 0 || param->paramid > context->nargs)
                        elog(ERROR, "invalid paramid: %d", param->paramid);
 
@@ -2272,7 +2957,7 @@ substitute_actual_parameters_mutator(Node *node,
 static void
 sql_inline_error_callback(void *arg)
 {
-       HeapTuple func_tuple = (HeapTuple) arg;
+       HeapTuple       func_tuple = (HeapTuple) arg;
        Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
        int                     syntaxerrposition;
 
@@ -2333,8 +3018,8 @@ evaluate_expr(Expr *expr, Oid result_type)
         *
         * 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?
+        * 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),
@@ -2418,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
@@ -2448,7 +3132,7 @@ 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
+ * 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
@@ -2464,8 +3148,8 @@ expression_tree_walker(Node *node,
        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
@@ -2486,10 +3170,19 @@ expression_tree_walker(Node *node,
                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;
@@ -2557,13 +3250,12 @@ 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);
                        }
@@ -2572,9 +3264,8 @@ expression_tree_walker(Node *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,
@@ -2586,7 +3277,7 @@ expression_tree_walker(Node *node,
                        return walker(((FieldSelect *) node)->arg, context);
                case T_FieldStore:
                        {
-                               FieldStore   *fstore = (FieldStore *) node;
+                               FieldStore *fstore = (FieldStore *) node;
 
                                if (walker(fstore->arg, context))
                                        return true;
@@ -2596,6 +3287,8 @@ expression_tree_walker(Node *node,
                        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;
@@ -2621,8 +3314,31 @@ expression_tree_walker(Node *node,
                        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:
@@ -2688,6 +3404,15 @@ 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, "unrecognized node type: %d",
                                 (int) nodeTag(node));
@@ -2717,12 +3442,12 @@ query_tree_walker(Query *query,
                                  void *context,
                                  int flags)
 {
-       ListCell   *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))
@@ -2733,9 +3458,57 @@ query_tree_walker(Query *query,
                return true;
        if (walker(query->limitCount, context))
                return true;
-       if (walker(query->in_info_list, context))
+       if (range_table_walker(query->rtable, walker, context, flags))
                return true;
-       foreach(rt, query->rtable)
+       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);
 
@@ -2759,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;
@@ -2810,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
@@ -2819,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
@@ -2834,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)  \
@@ -2865,7 +3642,8 @@ expression_tree_mutator(Node *node,
                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:
                        {
@@ -2873,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;
@@ -2950,11 +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;
@@ -2966,8 +3744,8 @@ expression_tree_mutator(Node *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 */
@@ -3006,6 +3784,16 @@ 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;
@@ -3041,14 +3829,25 @@ expression_tree_mutator(Node *node,
                        break;
                case T_RowExpr:
                        {
-                               RowExpr    *rowexpr = (RowExpr *) node;
-                               RowExpr    *newnode;
+                               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;
@@ -3059,6 +3858,28 @@ 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;
@@ -3101,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;
 
@@ -3119,10 +3936,9 @@ 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;
                                ListCell   *temp;
@@ -3179,6 +3995,17 @@ expression_tree_mutator(Node *node,
 
                                FLATCOPY(newnode, ininfo, InClauseInfo);
                                MUTATE(newnode->sub_targetlist, ininfo->sub_targetlist, List *);
+                               /* Assume we need not make a copy of in_operators list */
+                               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;
@@ -3217,9 +4044,6 @@ query_tree_mutator(Query *query,
                                   void *context,
                                   int flags)
 {
-       List       *newrt;
-       ListCell   *rt;
-
        Assert(query != NULL && IsA(query, Query));
 
        if (!(flags & QTW_DONT_COPY_QUERY))
@@ -3231,14 +4055,32 @@ query_tree_mutator(Query *query,
        }
 
        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->limitOffset, query->limitOffset, Node *);
        MUTATE(query->limitCount, query->limitCount, Node *);
-       MUTATE(query->in_info_list, query->in_info_list, List *);
-       newrt = NIL;
-       foreach(rt, query->rtable)
+       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;
@@ -3256,21 +4098,31 @@ query_tree_mutator(Query *query,
                                        CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
                                        MUTATE(newrte->subquery, newrte->subquery, Query *);
                                }
+                               else
+                               {
+                                       /* else, copy RT subqueries as-is */
+                                       newrte->subquery = copyObject(rte->subquery);
+                               }
                                break;
                        case RTE_JOIN:
                                if (!(flags & QTW_IGNORE_JOINALIASES))
-                               {
                                        MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
+                               else
+                               {
+                                       /* else, copy join aliases as-is */
+                                       newrte->joinaliasvars = copyObject(rte->joinaliasvars);
                                }
                                break;
                        case RTE_FUNCTION:
                                MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
                                break;
+                       case RTE_VALUES:
+                               MUTATE(newrte->values_lists, rte->values_lists, List *);
+                               break;
                }
                newrt = lappend(newrt, newrte);
        }
-       query->rtable = newrt;
-       return query;
+       return newrt;
 }
 
 /*