]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/plan/planner.c
Get rid of long-since-vestigial Iter node type, in favor of adding a
[postgresql] / src / backend / optimizer / plan / planner.c
index d2ec1b8a750ff6d0618d226cab9471e207381ff8..500297f2155fd87dc238b8078b93d499603e9265 100644 (file)
@@ -3,12 +3,12 @@
  * planner.c
  *       The query optimizer external interface.
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.99 2001/01/18 07:12:37 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.117 2002/05/12 23:43:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,9 @@
 
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
+#ifdef OPTIMIZER_DEBUG
+#include "nodes/print.h"
+#endif
 #include "optimizer/clauses.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 
 
 /* Expression kind codes for preprocess_expression */
-#define EXPRKIND_TARGET        0
+#define EXPRKIND_TARGET 0
 #define EXPRKIND_WHERE 1
-#define EXPRKIND_HAVING        2
+#define EXPRKIND_HAVING 2
 
 
-static Node *pull_up_subqueries(Query *parse, Node *jtnode);
+static Node *pull_up_subqueries(Query *parse, Node *jtnode,
+                                                               bool below_outer_join);
 static bool is_simple_subquery(Query *subquery);
+static bool has_nullable_targetlist(Query *subquery);
 static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
 static Node *preprocess_jointree(Query *parse, Node *jtnode);
 static Node *preprocess_expression(Query *parse, Node *expr, int kind);
@@ -48,7 +53,8 @@ static Plan *inheritance_planner(Query *parse, List *inheritlist);
 static Plan *grouping_planner(Query *parse, double tuple_fraction);
 static List *make_subplanTargetList(Query *parse, List *tlist,
                                           AttrNumber **groupColIdx);
-static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
+static Plan *make_groupplan(Query *parse,
+                          List *group_tlist, bool tuplePerGroup,
                           List *groupClause, AttrNumber *grpColIdx,
                           bool is_presorted, Plan *subplan);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
@@ -68,16 +74,16 @@ planner(Query *parse)
 
        /*
         * The planner can be called recursively (an example is when
-        * eval_const_expressions tries to pre-evaluate an SQL function).
-        * So, these global state variables must be saved and restored.
+        * eval_const_expressions tries to pre-evaluate an SQL function). So,
+        * these global state variables must be saved and restored.
         *
-        * These vars cannot be moved into the Query structure since their
-        * whole purpose is communication across multiple sub-Queries.
+        * These vars cannot be moved into the Query structure since their whole
+        * purpose is communication across multiple sub-Queries.
         *
         * Note we do NOT save and restore PlannerPlanId: it exists to assign
-        * unique IDs to SubPlan nodes, and we want those IDs to be unique
-        * for the life of a backend.  Also, PlannerInitPlan is saved/restored
-        * in subquery_planner, not here.
+        * unique IDs to SubPlan nodes, and we want those IDs to be unique for
+        * the life of a backend.  Also, PlannerInitPlan is saved/restored in
+        * subquery_planner, not here.
         */
        save_PlannerQueryLevel = PlannerQueryLevel;
        save_PlannerParamVar = PlannerParamVar;
@@ -95,7 +101,7 @@ planner(Query *parse)
        result_plan->nParamExec = length(PlannerParamVar);
 
        /* final cleanup of the plan */
-       set_plan_references(result_plan);
+       set_plan_references(parse, result_plan);
 
        /* restore state for outer planner, if any */
        PlannerQueryLevel = save_PlannerQueryLevel;
@@ -149,7 +155,8 @@ subquery_planner(Query *parse, double tuple_fraction)
         * this query.
         */
        parse->jointree = (FromExpr *)
-               pull_up_subqueries(parse, (Node *) parse->jointree);
+               pull_up_subqueries(parse, (Node *) parse->jointree, false);
+
        /*
         * If so, we may have created opportunities to simplify the jointree.
         */
@@ -168,28 +175,39 @@ subquery_planner(Query *parse, double tuple_fraction)
        parse->havingQual = preprocess_expression(parse, parse->havingQual,
                                                                                          EXPRKIND_HAVING);
 
+       /*
+        * Check for ungrouped variables passed to subplans in targetlist and
+        * HAVING clause (but not in WHERE or JOIN/ON clauses, since those are
+        * evaluated before grouping).  We can't do this any earlier because
+        * we must use the preprocessed targetlist for comparisons of grouped
+        * expressions.
+        */
+       if (parse->hasSubLinks &&
+               (parse->groupClause != NIL || parse->hasAggs))
+               check_subplans_for_ungrouped_vars(parse);
+
        /*
         * A HAVING clause without aggregates is equivalent to a WHERE clause
-        * (except it can only refer to grouped fields).  Transfer any agg-free
-        * clauses of the HAVING qual into WHERE.  This may seem like wasting
-        * cycles to cater to stupidly-written queries, but there are other
-        * reasons for doing it.  Firstly, if the query contains no aggs at all,
-        * then we aren't going to generate an Agg plan node, and so there'll be
-        * no place to execute HAVING conditions; without this transfer, we'd
-        * lose the HAVING condition entirely, which is wrong.  Secondly, when
-        * we push down a qual condition into a sub-query, it's easiest to push
-        * the qual into HAVING always, in case it contains aggs, and then let
-        * this code sort it out.
+        * (except it can only refer to grouped fields).  Transfer any
+        * agg-free clauses of the HAVING qual into WHERE.      This may seem like
+        * wasting cycles to cater to stupidly-written queries, but there are
+        * other reasons for doing it.  Firstly, if the query contains no aggs
+        * at all, then we aren't going to generate an Agg plan node, and so
+        * there'll be no place to execute HAVING conditions; without this
+        * transfer, we'd lose the HAVING condition entirely, which is wrong.
+        * Secondly, when we push down a qual condition into a sub-query, it's
+        * easiest to push the qual into HAVING always, in case it contains
+        * aggs, and then let this code sort it out.
         *
         * Note that both havingQual and parse->jointree->quals are in
         * implicitly-ANDed-list form at this point, even though they are
-        * declared as Node *.  Also note that contain_agg_clause does not
+        * declared as Node *.  Also note that contain_agg_clause does not
         * recurse into sub-selects, which is exactly what we need here.
         */
        newHaving = NIL;
        foreach(lst, (List *) parse->havingQual)
        {
-               Node   *havingclause = (Node *) lfirst(lst);
+               Node       *havingclause = (Node *) lfirst(lst);
 
                if (contain_agg_clause(havingclause))
                        newHaving = lappend(newHaving, havingclause);
@@ -201,30 +219,33 @@ subquery_planner(Query *parse, double tuple_fraction)
 
        /*
         * Do the main planning.  If we have an inherited target relation,
-        * that needs special processing, else go straight to grouping_planner.
+        * that needs special processing, else go straight to
+        * grouping_planner.
         */
        if (parse->resultRelation &&
-               (lst = expand_inherted_rtentry(parse, parse->resultRelation)) != NIL)
+        (lst = expand_inherted_rtentry(parse, parse->resultRelation, false))
+               != NIL)
                plan = inheritance_planner(parse, lst);
        else
                plan = grouping_planner(parse, tuple_fraction);
 
        /*
-        * If any subplans were generated, or if we're inside a subplan,
-        * build subPlan, extParam and locParam lists for plan nodes.
+        * If any subplans were generated, or if we're inside a subplan, build
+        * subPlan, extParam and locParam lists for plan nodes.
         */
        if (PlannerPlanId != saved_planid || PlannerQueryLevel > 1)
        {
                (void) SS_finalize_plan(plan);
+
                /*
-                * At the moment, SS_finalize_plan doesn't handle initPlans
-                * and so we assign them to the topmost plan node.
+                * At the moment, SS_finalize_plan doesn't handle initPlans and so
+                * we assign them to the topmost plan node.
                 */
                plan->initPlan = PlannerInitPlan;
                /* Must add the initPlans' extParams to the topmost node's, too */
                foreach(lst, plan->initPlan)
                {
-                       SubPlan *subplan = (SubPlan *) lfirst(lst);
+                       SubPlan    *subplan = (SubPlan *) lfirst(lst);
 
                        plan->extParam = set_unioni(plan->extParam,
                                                                                subplan->plan->extParam);
@@ -245,6 +266,9 @@ subquery_planner(Query *parse, double tuple_fraction)
  *             the parent query.  If the subquery has no special features like
  *             grouping/aggregation then we can merge it into the parent's jointree.
  *
+ * below_outer_join is true if this jointree node is within the nullable
+ * side of an outer join.  This restricts what we can do.
+ *
  * A tricky aspect of this code is that if we pull up a subquery we have
  * to replace Vars that reference the subquery's outputs throughout the
  * parent query, including quals attached to jointree nodes above the one
@@ -255,7 +279,7 @@ subquery_planner(Query *parse, double tuple_fraction)
  * copy of the tree; we have to invoke it just on the quals, instead.
  */
 static Node *
-pull_up_subqueries(Query *parse, Node *jtnode)
+pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
 {
        if (jtnode == NULL)
                return NULL;
@@ -266,59 +290,97 @@ pull_up_subqueries(Query *parse, Node *jtnode)
                Query      *subquery = rte->subquery;
 
                /*
-                * Is this a subquery RTE, and if so, is the subquery simple enough
-                * to pull up?  (If not, do nothing at this node.)
+                * Is this a subquery RTE, and if so, is the subquery simple
+                * enough to pull up?  (If not, do nothing at this node.)
+                *
+                * If we are inside an outer join, only pull up subqueries whose
+                * targetlists are nullable --- otherwise substituting their tlist
+                * entries for upper Var references would do the wrong thing
+                * (the results wouldn't become NULL when they're supposed to).
+                * XXX This could be improved by generating pseudo-variables for
+                * such expressions; we'd have to figure out how to get the pseudo-
+                * variables evaluated at the right place in the modified plan tree.
+                * Fix it someday.
+                *
+                * Note: even if the subquery itself is simple enough, we can't pull
+                * it up if there is a reference to its whole tuple result.  Perhaps
+                * a pseudo-variable is the answer here too.
                 */
-               if (subquery && is_simple_subquery(subquery))
+               if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(subquery) &&
+                       (!below_outer_join || has_nullable_targetlist(subquery)) &&
+                       !contain_whole_tuple_var((Node *) parse, varno, 0))
                {
-                       int             rtoffset;
-                       Node   *subjointree;
-                       List   *subtlist;
-                       List   *l;
+                       int                     rtoffset;
+                       List       *subtlist;
+                       List       *rt;
 
                        /*
-                        * First, recursively pull up the subquery's subqueries,
-                        * so that this routine's processing is complete for its
-                        * jointree and rangetable.
+                        * First, recursively pull up the subquery's subqueries, so
+                        * that this routine's processing is complete for its jointree
+                        * and rangetable.      NB: if the same subquery is referenced
+                        * from multiple jointree items (which can't happen normally,
+                        * but might after rule rewriting), then we will invoke this
+                        * processing multiple times on that subquery.  OK because
+                        * nothing will happen after the first time.  We do have to be
+                        * careful to copy everything we pull up, however, or risk
+                        * having chunks of structure multiply linked.
                         */
                        subquery->jointree = (FromExpr *)
-                               pull_up_subqueries(subquery, (Node *) subquery->jointree);
+                               pull_up_subqueries(subquery, (Node *) subquery->jointree,
+                                                                  below_outer_join);
+
                        /*
-                        * Append the subquery's rangetable to mine (currently,
-                        * no adjustments will be needed in the subquery's rtable).
+                        * Now make a modifiable copy of the subquery that we can
+                        * run OffsetVarNodes on.
                         */
-                       rtoffset = length(parse->rtable);
-                       parse->rtable = nconc(parse->rtable, subquery->rtable);
+                       subquery = copyObject(subquery);
+
                        /*
-                        * Make copies of the subquery's jointree and targetlist
-                        * with varnos adjusted to match the merged rangetable.
+                        * Adjust varnos in subquery so that we can append its
+                        * rangetable to upper query's.
                         */
-                       subjointree = copyObject(subquery->jointree);
-                       OffsetVarNodes(subjointree, rtoffset, 0);
-                       subtlist = copyObject(subquery->targetList);
-                       OffsetVarNodes((Node *) subtlist, rtoffset, 0);
+                       rtoffset = length(parse->rtable);
+                       OffsetVarNodes((Node *) subquery, rtoffset, 0);
+
                        /*
                         * Replace all of the top query's references to the subquery's
                         * outputs with copies of the adjusted subtlist items, being
                         * careful not to replace any of the jointree structure.
+                        * (This'd be a lot cleaner if we could use query_tree_mutator.)
                         */
+                       subtlist = subquery->targetList;
                        parse->targetList = (List *)
                                ResolveNew((Node *) parse->targetList,
                                                   varno, 0, subtlist, CMD_SELECT, 0);
                        resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist);
+                       Assert(parse->setOperations == NULL);
                        parse->havingQual =
                                ResolveNew(parse->havingQual,
                                                   varno, 0, subtlist, CMD_SELECT, 0);
-                       /*
-                        * Pull up any FOR UPDATE markers, too.
-                        */
-                       foreach(l, subquery->rowMarks)
+
+                       foreach(rt, parse->rtable)
                        {
-                               int             submark = lfirsti(l);
+                               RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
-                               parse->rowMarks = lappendi(parse->rowMarks,
-                                                                                  submark + rtoffset);
+                               if (rte->rtekind == RTE_JOIN)
+                                       rte->joinaliasvars = (List *)
+                                               ResolveNew((Node *) rte->joinaliasvars,
+                                                                  varno, 0, subtlist, CMD_SELECT, 0);
                        }
+
+                       /*
+                        * Now append the adjusted rtable entries to upper query.
+                        * (We hold off until after fixing the upper rtable entries;
+                        * no point in running that code on the subquery ones too.)
+                        */
+                       parse->rtable = nconc(parse->rtable, subquery->rtable);
+
+                       /*
+                        * Pull up any FOR UPDATE markers, too.  (OffsetVarNodes
+                        * already adjusted the marker values, so just nconc the list.)
+                        */
+                       parse->rowMarks = nconc(parse->rowMarks, subquery->rowMarks);
+
                        /*
                         * Miscellaneous housekeeping.
                         */
@@ -329,7 +391,7 @@ pull_up_subqueries(Query *parse, Node *jtnode)
                         * Return the adjusted subquery jointree to replace the
                         * RangeTblRef entry in my jointree.
                         */
-                       return subjointree;
+                       return (Node *) subquery->jointree;
                }
        }
        else if (IsA(jtnode, FromExpr))
@@ -338,16 +400,53 @@ pull_up_subqueries(Query *parse, Node *jtnode)
                List       *l;
 
                foreach(l, f->fromlist)
-               {
-                       lfirst(l) = pull_up_subqueries(parse, lfirst(l));
-               }
+                       lfirst(l) = pull_up_subqueries(parse, lfirst(l),
+                                                                                  below_outer_join);
        }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
 
-               j->larg = pull_up_subqueries(parse, j->larg);
-               j->rarg = pull_up_subqueries(parse, j->rarg);
+               /* Recurse, being careful to tell myself when inside outer join */
+               switch (j->jointype)
+               {
+                       case JOIN_INNER:
+                               j->larg = pull_up_subqueries(parse, j->larg,
+                                                                                        below_outer_join);
+                               j->rarg = pull_up_subqueries(parse, j->rarg,
+                                                                                        below_outer_join);
+                               break;
+                       case JOIN_LEFT:
+                               j->larg = pull_up_subqueries(parse, j->larg,
+                                                                                        below_outer_join);
+                               j->rarg = pull_up_subqueries(parse, j->rarg,
+                                                                                        true);
+                               break;
+                       case JOIN_FULL:
+                               j->larg = pull_up_subqueries(parse, j->larg,
+                                                                                        true);
+                               j->rarg = pull_up_subqueries(parse, j->rarg,
+                                                                                        true);
+                               break;
+                       case JOIN_RIGHT:
+                               j->larg = pull_up_subqueries(parse, j->larg,
+                                                                                        true);
+                               j->rarg = pull_up_subqueries(parse, j->rarg,
+                                                                                        below_outer_join);
+                               break;
+                       case JOIN_UNION:
+
+                               /*
+                                * This is where we fail if upper levels of planner
+                                * haven't rewritten UNION JOIN as an Append ...
+                                */
+                               elog(ERROR, "UNION JOIN is not implemented yet");
+                               break;
+                       default:
+                               elog(ERROR, "pull_up_subqueries: unexpected join type %d",
+                                        j->jointype);
+                               break;
+               }
        }
        else
                elog(ERROR, "pull_up_subqueries: unexpected node type %d",
@@ -372,12 +471,14 @@ is_simple_subquery(Query *subquery)
                subquery->into != NULL ||
                subquery->isPortal)
                elog(ERROR, "is_simple_subquery: subquery is bogus");
+
        /*
-        * Can't currently pull up a query with setops.
-        * Maybe after querytree redesign...
+        * Can't currently pull up a query with setops. Maybe after querytree
+        * redesign...
         */
        if (subquery->setOperations)
                return false;
+
        /*
         * Can't pull up a subquery involving grouping, aggregation, sorting,
         * or limiting.
@@ -390,12 +491,22 @@ is_simple_subquery(Query *subquery)
                subquery->limitOffset ||
                subquery->limitCount)
                return false;
+
+       /*
+        * Don't pull up a subquery that has any set-returning functions in
+        * its targetlist.  Otherwise we might well wind up inserting
+        * set-returning functions into places where they mustn't go,
+        * such as quals of higher queries.
+        */
+       if (expression_returns_set((Node *) subquery->targetList))
+               return false;
+
        /*
         * Hack: don't try to pull up a subquery with an empty jointree.
         * query_planner() will correctly generate a Result plan for a
         * jointree that's totally empty, but I don't think the right things
-        * happen if an empty FromExpr appears lower down in a jointree.
-        * Not worth working hard on this, just to collapse SubqueryScan/Result
+        * happen if an empty FromExpr appears lower down in a jointree. Not
+        * worth working hard on this, just to collapse SubqueryScan/Result
         * into Result...
         */
        if (subquery->jointree->fromlist == NIL)
@@ -404,6 +515,37 @@ is_simple_subquery(Query *subquery)
        return true;
 }
 
+/*
+ * has_nullable_targetlist
+ *       Check a subquery in the range table to see if all the non-junk
+ *       targetlist items are simple variables (and, hence, will correctly
+ *       go to NULL when examined above the point of an outer join).
+ *
+ * A possible future extension is to accept strict functions of simple
+ * variables, eg, "x + 1".
+ */
+static bool
+has_nullable_targetlist(Query *subquery)
+{
+       List       *l;
+
+       foreach(l, subquery->targetList)
+       {
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+               /* ignore resjunk columns */
+               if (tle->resdom->resjunk)
+                       continue;
+
+               /* Okay if tlist item is a simple Var */
+               if (tle->expr && IsA(tle->expr, Var))
+                       continue;
+
+               return false;
+       }
+       return true;
+}
+
 /*
  * Helper routine for pull_up_subqueries: do ResolveNew on every expression
  * in the jointree, without changing the jointree structure itself.  Ugly,
@@ -436,7 +578,9 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
                resolvenew_in_jointree(j->rarg, varno, subtlist);
                j->quals = ResolveNew(j->quals,
                                                          varno, 0, subtlist, CMD_SELECT, 0);
-               /* We don't bother to update the colvars list, since it won't be
+
+               /*
+                * We don't bother to update the colvars list, since it won't be
                 * used again ...
                 */
        }
@@ -451,13 +595,13 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
  *
  * If we succeed in pulling up a subquery then we might form a jointree
  * in which a FromExpr is a direct child of another FromExpr.  In that
- * case we can consider collapsing the two FromExprs into one.  This is
+ * case we can consider collapsing the two FromExprs into one. This is
  * an optional conversion, since the planner will work correctly either
  * way.  But we may find a better plan (at the cost of more planning time)
  * if we merge the two nodes.
  *
  * NOTE: don't try to do this in the same jointree scan that does subquery
- * pullup!  Since we're changing the jointree structure here, that wouldn't
+ * pullup!     Since we're changing the jointree structure here, that wouldn't
  * work reliably --- see comments for pull_up_subqueries().
  */
 static Node *
@@ -477,7 +621,7 @@ preprocess_jointree(Query *parse, Node *jtnode)
 
                foreach(l, f->fromlist)
                {
-                       Node   *child = (Node *) lfirst(l);
+                       Node       *child = (Node *) lfirst(l);
 
                        /* Recursively simplify the child... */
                        child = preprocess_jointree(parse, child);
@@ -485,19 +629,20 @@ preprocess_jointree(Query *parse, Node *jtnode)
                        if (child && IsA(child, FromExpr))
                        {
                                /*
-                                * Yes, so do we want to merge it into parent?  Always do so
-                                * if child has just one element (since that doesn't make the
-                                * parent's list any longer).  Otherwise we have to be careful
-                                * about the increase in planning time caused by combining the
-                                * two join search spaces into one.  Our heuristic is to merge
-                                * if the merge will produce a join list no longer than
-                                * GEQO_RELS/2.  (Perhaps need an additional user parameter?)
+                                * Yes, so do we want to merge it into parent?  Always do
+                                * so if child has just one element (since that doesn't
+                                * make the parent's list any longer).  Otherwise we have
+                                * to be careful about the increase in planning time
+                                * caused by combining the two join search spaces into
+                                * one.  Our heuristic is to merge if the merge will
+                                * produce a join list no longer than GEQO_RELS/2.
+                                * (Perhaps need an additional user parameter?)
                                 */
                                FromExpr   *subf = (FromExpr *) child;
                                int                     childlen = length(subf->fromlist);
                                int                     myothers = length(newlist) + length(lnext(l));
 
-                               if (childlen <= 1 || (childlen+myothers) <= geqo_rels/2)
+                               if (childlen <= 1 || (childlen + myothers) <= geqo_rels / 2)
                                {
                                        newlist = nconc(newlist, subf->fromlist);
                                        f->quals = make_and_qual(f->quals, subf->quals);
@@ -533,6 +678,9 @@ preprocess_jointree(Query *parse, Node *jtnode)
 static Node *
 preprocess_expression(Query *parse, Node *expr, int kind)
 {
+       bool            has_join_rtes;
+       List       *rt;
+
        /*
         * Simplify constant expressions.
         *
@@ -544,8 +692,8 @@ preprocess_expression(Query *parse, Node *expr, int kind)
        expr = eval_const_expressions(expr);
 
        /*
-        * If it's a qual or havingQual, canonicalize it, and convert it
-        * to implicit-AND format.
+        * If it's a qual or havingQual, canonicalize it, and convert it to
+        * implicit-AND format.
         *
         * XXX Is there any value in re-applying eval_const_expressions after
         * canonicalize_qual?
@@ -560,27 +708,37 @@ preprocess_expression(Query *parse, Node *expr, int kind)
 #endif
        }
 
+       /* Expand SubLinks to SubPlans */
        if (parse->hasSubLinks)
-       {
-               /* Expand SubLinks to SubPlans */
                expr = SS_process_sublinks(expr);
 
-               if (kind != EXPRKIND_WHERE &&
-                       (parse->groupClause != NIL || parse->hasAggs))
-               {
-                       /*
-                        * Check for ungrouped variables passed to subplans.  Note we
-                        * do NOT do this for subplans in WHERE (or JOIN/ON); it's legal
-                        * there because WHERE is evaluated pre-GROUP.
-                        */
-                       check_subplans_for_ungrouped_vars(expr, parse);
-               }
-       }
-
        /* Replace uplevel vars with Param nodes */
        if (PlannerQueryLevel > 1)
                expr = SS_replace_correlation_vars(expr);
 
+       /*
+        * If the query has any join RTEs, try to replace join alias variables
+        * with base-relation variables, to allow quals to be pushed down.
+        * We must do this after sublink processing, since it does not recurse
+        * into sublinks.
+        *
+        * The flattening pass is expensive enough that it seems worthwhile to
+        * scan the rangetable to see if we can avoid it.
+        */
+       has_join_rtes = false;
+       foreach(rt, parse->rtable)
+       {
+               RangeTblEntry *rte = lfirst(rt);
+
+               if (rte->rtekind == RTE_JOIN)
+               {
+                       has_join_rtes = true;
+                       break;
+               }
+       }
+       if (has_join_rtes)
+               expr = flatten_join_alias_vars(expr, parse, false);
+
        return expr;
 }
 
@@ -628,12 +786,12 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
  *       inheritance set.
  *
  * We have to handle this case differently from cases where a source
- * relation is an inheritance set.  Source inheritance is expanded at
+ * relation is an inheritance set.     Source inheritance is expanded at
  * the bottom of the plan tree (see allpaths.c), but target inheritance
  * has to be expanded at the top.  The reason is that for UPDATE, each
  * target relation needs a different targetlist matching its own column
  * set.  (This is not so critical for DELETE, but for simplicity we treat
- * inherited DELETE the same way.)  Fortunately, the UPDATE/DELETE target
+ * inherited DELETE the same way.)     Fortunately, the UPDATE/DELETE target
  * can never be the nullable side of an outer join, so it's OK to generate
  * the plan this way.
  *
@@ -654,17 +812,17 @@ inheritance_planner(Query *parse, List *inheritlist)
 
        foreach(l, inheritlist)
        {
-               int             childRTindex = lfirsti(l);
-               Oid             childOID = getrelid(childRTindex, parse->rtable);
-               Query  *subquery;
-               Plan   *subplan;
+               int                     childRTindex = lfirsti(l);
+               Oid                     childOID = getrelid(childRTindex, parse->rtable);
+               Query      *subquery;
+               Plan       *subplan;
 
                /* Generate modified query with this rel as target */
                subquery = (Query *) adjust_inherited_attrs((Node *) parse,
-                                                                                                       parentRTindex, parentOID,
-                                                                                                       childRTindex, childOID);
+                                                                                               parentRTindex, parentOID,
+                                                                                                childRTindex, childOID);
                /* Generate plan */
-               subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */);
+               subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */ );
                subplans = lappend(subplans, subplan);
                /* Save preprocessed tlist from first rel for use in Append */
                if (tlist == NIL)
@@ -729,17 +887,17 @@ grouping_planner(Query *parse, double tuple_fraction)
                tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
 
                /*
-                * Can't handle FOR UPDATE here (parser should have checked already,
-                * but let's make sure).
+                * Can't handle FOR UPDATE here (parser should have checked
+                * already, but let's make sure).
                 */
                if (parse->rowMarks)
                        elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
 
                /*
                 * We set current_pathkeys NIL indicating we do not know sort
-                * order.  This is correct when the top set operation is UNION ALL,
-                * since the appended-together results are unsorted even if the
-                * subplans were sorted.  For other set operations we could be
+                * order.  This is correct when the top set operation is UNION
+                * ALL, since the appended-together results are unsorted even if
+                * the subplans were sorted.  For other set operations we could be
                 * smarter --- room for future improvement!
                 */
                current_pathkeys = NIL;
@@ -765,22 +923,26 @@ grouping_planner(Query *parse, double tuple_fraction)
 
                /*
                 * Add TID targets for rels selected FOR UPDATE (should this be
-                * done in preprocess_targetlist?).  The executor uses the TID
-                * to know which rows to lock, much as for UPDATE or DELETE.
+                * done in preprocess_targetlist?).  The executor uses the TID to
+                * know which rows to lock, much as for UPDATE or DELETE.
                 */
                if (parse->rowMarks)
                {
                        List       *l;
 
                        /*
-                        * We've got trouble if the FOR UPDATE appears inside grouping,
-                        * since grouping renders a reference to individual tuple CTIDs
-                        * invalid.  This is also checked at parse time, but that's
-                        * insufficient because of rule substitution, query pullup, etc.
+                        * We've got trouble if the FOR UPDATE appears inside
+                        * grouping, since grouping renders a reference to individual
+                        * tuple CTIDs invalid.  This is also checked at parse time,
+                        * but that's insufficient because of rule substitution, query
+                        * pullup, etc.
                         */
                        CheckSelectForUpdate(parse);
 
-                       /* Currently the executor only supports FOR UPDATE at top level */
+                       /*
+                        * Currently the executor only supports FOR UPDATE at top
+                        * level
+                        */
                        if (PlannerQueryLevel > 1)
                                elog(ERROR, "SELECT FOR UPDATE is not allowed in subselects");
 
@@ -846,9 +1008,9 @@ grouping_planner(Query *parse, double tuple_fraction)
 
                /*
                 * Figure out whether we expect to retrieve all the tuples that
-                * the plan can generate, or to stop early due to a LIMIT or other
-                * factors.  If the caller passed a value >= 0, believe that
-                * value, else do our own examination of the query context.
+                * the plan can generate, or to stop early due to outside factors
+                * such as a cursor.  If the caller passed a value >= 0, believe
+                * that value, else do our own examination of the query context.
                 */
                if (tuple_fraction < 0.0)
                {
@@ -856,75 +1018,121 @@ grouping_planner(Query *parse, double tuple_fraction)
                        tuple_fraction = 0.0;
 
                        /*
-                        * Check for a LIMIT clause.
+                        * Check for retrieve-into-portal, ie DECLARE CURSOR.
+                        *
+                        * We have no real idea how many tuples the user will ultimately
+                        * FETCH from a cursor, but it seems a good bet that he
+                        * doesn't want 'em all.  Optimize for 10% retrieval (you
+                        * gotta better number?  Should this be a SETtable parameter?)
+                        */
+                       if (parse->isPortal)
+                               tuple_fraction = 0.10;
+               }
+
+               /*
+                * Adjust tuple_fraction if we see that we are going to apply
+                * limiting/grouping/aggregation/etc.  This is not overridable by
+                * the caller, since it reflects plan actions that this routine
+                * will certainly take, not assumptions about context.
+                */
+               if (parse->limitCount != NULL)
+               {
+                       /*
+                        * A LIMIT clause limits the absolute number of tuples
+                        * returned. However, if it's not a constant LIMIT then we
+                        * have to punt; for lack of a better idea, assume 10% of the
+                        * plan's result is wanted.
                         */
-                       if (parse->limitCount != NULL)
+                       double          limit_fraction = 0.0;
+
+                       if (IsA(parse->limitCount, Const))
                        {
-                               if (IsA(parse->limitCount, Const))
+                               Const      *limitc = (Const *) parse->limitCount;
+                               int32           count = DatumGetInt32(limitc->constvalue);
+
+                               /*
+                                * A NULL-constant LIMIT represents "LIMIT ALL", which we
+                                * treat the same as no limit (ie, expect to retrieve all
+                                * the tuples).
+                                */
+                               if (!limitc->constisnull && count > 0)
                                {
-                                       Const      *limitc = (Const *) parse->limitCount;
-                                       int32           count = DatumGetInt32(limitc->constvalue);
-
-                                       /*
-                                        * A NULL-constant LIMIT represents "LIMIT ALL",
-                                        * which we treat the same as no limit (ie,
-                                        * expect to retrieve all the tuples).
-                                        */
-                                       if (!limitc->constisnull && count > 0)
+                                       limit_fraction = (double) count;
+                                       /* We must also consider the OFFSET, if present */
+                                       if (parse->limitOffset != NULL)
                                        {
-                                               tuple_fraction = (double) count;
-                                               /* We must also consider the OFFSET, if present */
-                                               if (parse->limitOffset != NULL)
+                                               if (IsA(parse->limitOffset, Const))
+                                               {
+                                                       int32           offset;
+
+                                                       limitc = (Const *) parse->limitOffset;
+                                                       offset = DatumGetInt32(limitc->constvalue);
+                                                       if (!limitc->constisnull && offset > 0)
+                                                               limit_fraction += (double) offset;
+                                               }
+                                               else
                                                {
-                                                       if (IsA(parse->limitOffset, Const))
-                                                       {
-                                                               int32           offset;
-
-                                                               limitc = (Const *) parse->limitOffset;
-                                                               offset = DatumGetInt32(limitc->constvalue);
-                                                               if (!limitc->constisnull && offset > 0)
-                                                                       tuple_fraction += (double) offset;
-                                                       }
-                                                       else
-                                                       {
-                                                               /* It's an expression ... punt ... */
-                                                               tuple_fraction = 0.10;
-                                                       }
+                                                       /* OFFSET is an expression ... punt ... */
+                                                       limit_fraction = 0.10;
                                                }
                                        }
                                }
+                       }
+                       else
+                       {
+                               /* LIMIT is an expression ... punt ... */
+                               limit_fraction = 0.10;
+                       }
+
+                       if (limit_fraction > 0.0)
+                       {
+                               /*
+                                * If we have absolute limits from both caller and LIMIT,
+                                * use the smaller value; if one is fractional and the
+                                * other absolute, treat the fraction as a fraction of the
+                                * absolute value; else we can multiply the two fractions
+                                * together.
+                                */
+                               if (tuple_fraction >= 1.0)
+                               {
+                                       if (limit_fraction >= 1.0)
+                                       {
+                                               /* both absolute */
+                                               tuple_fraction = Min(tuple_fraction, limit_fraction);
+                                       }
+                                       else
+                                       {
+                                               /* caller absolute, limit fractional */
+                                               tuple_fraction *= limit_fraction;
+                                               if (tuple_fraction < 1.0)
+                                                       tuple_fraction = 1.0;
+                                       }
+                               }
+                               else if (tuple_fraction > 0.0)
+                               {
+                                       if (limit_fraction >= 1.0)
+                                       {
+                                               /* caller fractional, limit absolute */
+                                               tuple_fraction *= limit_fraction;
+                                               if (tuple_fraction < 1.0)
+                                                       tuple_fraction = 1.0;
+                                       }
+                                       else
+                                       {
+                                               /* both fractional */
+                                               tuple_fraction *= limit_fraction;
+                                       }
+                               }
                                else
                                {
-                                       /*
-                                        * COUNT is an expression ... don't know exactly what the
-                                        * limit will be, but for lack of a better idea assume
-                                        * 10% of the plan's result is wanted.
-                                        */
-                                       tuple_fraction = 0.10;
+                                       /* no info from caller, just use limit */
+                                       tuple_fraction = limit_fraction;
                                }
                        }
-
-                       /*
-                        * If no LIMIT, check for retrieve-into-portal, ie DECLARE CURSOR.
-                        *
-                        * We have no real idea how many tuples the user will ultimately
-                        * FETCH from a cursor, but it seems a good bet that he
-                        * doesn't want 'em all.  Optimize for 10% retrieval (you
-                        * gotta better number?)
-                        */
-                       else if (parse->isPortal)
-                               tuple_fraction = 0.10;
                }
 
-               /*
-                * Adjust tuple_fraction if we see that we are going to apply
-                * grouping/aggregation/etc.  This is not overridable by the
-                * caller, since it reflects plan actions that this routine will
-                * certainly take, not assumptions about context.
-                */
                if (parse->groupClause)
                {
-
                        /*
                         * In GROUP BY mode, we have the little problem that we don't
                         * really know how many input tuples will be needed to make a
@@ -953,7 +1161,6 @@ grouping_planner(Query *parse, double tuple_fraction)
                }
                else if (parse->hasAggs)
                {
-
                        /*
                         * Ungrouped aggregate will certainly want all the input
                         * tuples.
@@ -962,7 +1169,6 @@ grouping_planner(Query *parse, double tuple_fraction)
                }
                else if (parse->distinctClause)
                {
-
                        /*
                         * SELECT DISTINCT, like GROUP, will absorb an unpredictable
                         * number of input tuples per output tuple.  Handle the same
@@ -1011,12 +1217,11 @@ grouping_planner(Query *parse, double tuple_fraction)
 
                /*
                 * If there are aggregates then the Group node should just return
-                * the same set of vars as the subplan did (but we can exclude any
-                * GROUP BY expressions).  If there are no aggregates then the
-                * Group node had better compute the final tlist.
+                * the same set of vars as the subplan did.  If there are no aggs
+                * then the Group node had better compute the final tlist.
                 */
                if (parse->hasAggs)
-                       group_tlist = flatten_tlist(result_plan->targetlist);
+                       group_tlist = new_unsorted_tlist(result_plan->targetlist);
                else
                        group_tlist = tlist;
 
@@ -1031,7 +1236,6 @@ grouping_planner(Query *parse, double tuple_fraction)
                }
                else
                {
-
                        /*
                         * We will need to do an explicit sort by the GROUP BY clause.
                         * make_groupplan will do the work, but set current_pathkeys
@@ -1041,7 +1245,8 @@ grouping_planner(Query *parse, double tuple_fraction)
                        current_pathkeys = group_pathkeys;
                }
 
-               result_plan = make_groupplan(group_tlist,
+               result_plan = make_groupplan(parse,
+                                                                        group_tlist,
                                                                         tuplePerGroup,
                                                                         parse->groupClause,
                                                                         groupColIdx,
@@ -1074,7 +1279,7 @@ grouping_planner(Query *parse, double tuple_fraction)
        if (parse->sortClause)
        {
                if (!pathkeys_contained_in(sort_pathkeys, current_pathkeys))
-                       result_plan = make_sortplan(tlist, result_plan,
+                       result_plan = make_sortplan(parse, tlist, result_plan,
                                                                                parse->sortClause);
        }
 
@@ -1123,7 +1328,10 @@ grouping_planner(Query *parse, double tuple_fraction)
  * where the a+b target will be used by the Sort/Group steps, and the
  * other targets will be used for computing the final results. (In the
  * above example we could theoretically suppress the a and b targets and
- * use only a+b, but it's not really worth the trouble.)
+ * pass down only c,d,a+b, but it's not really worth the trouble to
+ * eliminate simple var references from the subplan.  We will avoid doing
+ * the extra computation to recompute a+b at the outer level; see
+ * replace_vars_with_subplan_refs() in setrefs.c.)
  *
  * 'parse' is the query being processed.
  * 'tlist' is the query's target list.
@@ -1217,7 +1425,8 @@ make_subplanTargetList(Query *parse,
  *             first add an explicit Sort node.
  */
 static Plan *
-make_groupplan(List *group_tlist,
+make_groupplan(Query *parse,
+                          List *group_tlist,
                           bool tuplePerGroup,
                           List *groupClause,
                           AttrNumber *grpColIdx,
@@ -1228,7 +1437,6 @@ make_groupplan(List *group_tlist,
 
        if (!is_presorted)
        {
-
                /*
                 * The Sort node always just takes a copy of the subplan's tlist
                 * plus ordering information.  (This might seem inefficient if the
@@ -1256,13 +1464,13 @@ make_groupplan(List *group_tlist,
                        {
                                /* OK, insert the ordering info needed by the executor. */
                                resdom->reskey = ++keyno;
-                               resdom->reskeyop = get_opcode(grpcl->sortop);
+                               resdom->reskeyop = grpcl->sortop;
                        }
                }
 
                Assert(keyno > 0);
 
-               subplan = (Plan *) make_sort(sort_tlist, subplan, keyno);
+               subplan = (Plan *) make_sort(parse, sort_tlist, subplan, keyno);
        }
 
        return (Plan *) make_group(group_tlist, tuplePerGroup, numCols,
@@ -1274,7 +1482,7 @@ make_groupplan(List *group_tlist,
  *       Add a Sort node to implement an explicit ORDER BY clause.
  */
 Plan *
-make_sortplan(List *tlist, Plan *plannode, List *sortcls)
+make_sortplan(Query *parse, List *tlist, Plan *plannode, List *sortcls)
 {
        List       *sort_tlist;
        List       *i;
@@ -1301,13 +1509,13 @@ make_sortplan(List *tlist, Plan *plannode, List *sortcls)
                {
                        /* OK, insert the ordering info needed by the executor. */
                        resdom->reskey = ++keyno;
-                       resdom->reskeyop = get_opcode(sortcl->sortop);
+                       resdom->reskeyop = sortcl->sortop;
                }
        }
 
        Assert(keyno > 0);
 
-       return (Plan *) make_sort(sort_tlist, plannode, keyno);
+       return (Plan *) make_sort(parse, sort_tlist, plannode, keyno);
 }
 
 /*