]> granicus.if.org Git - postgresql/commitdiff
Simplify query_planner's API by having it return the top-level RelOptInfo.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 5 Aug 2013 19:00:57 +0000 (15:00 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 5 Aug 2013 19:01:09 +0000 (15:01 -0400)
Formerly, query_planner returned one or possibly two Paths for the topmost
join relation, so that grouping_planner didn't see the join RelOptInfo
(at least not directly; it didn't have any hesitation about examining
cheapest_path->parent, though).  However, correct selection of the Paths
involved a significant amount of coupling between query_planner and
grouping_planner, a problem which has gotten worse over time.  It seems
best to give up on this API choice and instead return the topmost
RelOptInfo explicitly.  Then grouping_planner can pull out the Paths it
wants from the rel's path list.  In this way we can remove all knowledge
of grouping behaviors from query_planner.

The only real benefit of the old way is that in the case of an empty
FROM clause, we never made any RelOptInfos at all, just a Path.  Now
we have to gin up a dummy RelOptInfo to represent the empty FROM clause.
That's not a very big deal though.

While at it, simplify query_planner's API a bit more by having the caller
set up root->tuple_fraction and root->limit_tuples, rather than passing
those values as separate parameters.  Since query_planner no longer does
anything with either value, requiring it to fill the PlannerInfo fields
seemed pretty arbitrary.

This patch just rearranges code; it doesn't (intentionally) change any
behaviors.  Followup patches will do more interesting things.

src/backend/optimizer/README
src/backend/optimizer/plan/planagg.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/util/relnode.c
src/include/optimizer/pathnode.h
src/include/optimizer/planmain.h

index 751766fb9dd604e40700e76ef5b8ae3b6a6dd469..1e30cfd4e1e832dabdfec9e6ed887a0a73282aae 100644 (file)
@@ -372,10 +372,10 @@ generated during the optimization process are marked with their sort order
 
 It is also possible to avoid an explicit sort step to implement a user's
 ORDER BY clause if the final path has the right ordering already, so the
-sort ordering is of interest even at the top level.  query_planner() will
+sort ordering is of interest even at the top level.  grouping_planner() will
 look for the cheapest path with a sort order matching the desired order,
-and grouping_planner() will compare its cost to the cost of using the
-cheapest-overall path and doing an explicit sort.
+then compare its cost to the cost of using the cheapest-overall path and
+doing an explicit sort on that.
 
 When we are generating paths for a particular RelOptInfo, we discard a path
 if it is more expensive than another known path that has the same or better
index d098f3471206c1f0b689a9833dcd787ae75a641b..ef0c20f8f170971e066f68c748904f5a251bf9e1 100644 (file)
@@ -316,6 +316,7 @@ find_minmax_aggs_walker(Node *node, List **context)
                Assert(aggref->agglevelsup == 0);
                if (list_length(aggref->args) != 1)
                        return true;            /* it couldn't be MIN/MAX */
+
                /*
                 * ORDER BY is usually irrelevant for MIN/MAX, but it can change the
                 * outcome if the aggsortop's operator class recognizes non-identical
@@ -329,6 +330,7 @@ find_minmax_aggs_walker(Node *node, List **context)
                 */
                if (aggref->aggorder != NIL)
                        return true;
+
                /*
                 * We might implement the optimization when a FILTER clause is present
                 * by adding the filter to the quals of the generated subquery.
@@ -399,9 +401,8 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
        TargetEntry *tle;
        NullTest   *ntest;
        SortGroupClause *sortcl;
-       Path       *cheapest_path;
+       RelOptInfo *final_rel;
        Path       *sorted_path;
-       double          dNumGroups;
        Cost            path_cost;
        double          path_fraction;
 
@@ -470,25 +471,28 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
         * Generate the best paths for this query, telling query_planner that we
         * have LIMIT 1.
         */
-       query_planner(subroot, parse->targetList, 1.0, 1.0,
-                                 minmax_qp_callback, NULL,
-                                 &cheapest_path, &sorted_path, &dNumGroups);
+       subroot->tuple_fraction = 1.0;
+       subroot->limit_tuples = 1.0;
+
+       final_rel = query_planner(subroot, parse->targetList,
+                                                         minmax_qp_callback, NULL);
 
        /*
-        * Fail if no presorted path.  However, if query_planner determines that
-        * the presorted path is also the cheapest, it will set sorted_path to
-        * NULL ... don't be fooled.  (This is kind of a pain here, but it
-        * simplifies life for grouping_planner, so leave it be.)
+        * Get the best presorted path, that being the one that's cheapest for
+        * fetching just one row.  If there's no such path, fail.
         */
+       if (final_rel->rows > 1.0)
+               path_fraction = 1.0 / final_rel->rows;
+       else
+               path_fraction = 1.0;
+
+       sorted_path =
+               get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist,
+                                                                                                 subroot->query_pathkeys,
+                                                                                                 NULL,
+                                                                                                 path_fraction);
        if (!sorted_path)
-       {
-               if (cheapest_path &&
-                       pathkeys_contained_in(subroot->sort_pathkeys,
-                                                                 cheapest_path->pathkeys))
-                       sorted_path = cheapest_path;
-               else
-                       return false;
-       }
+               return false;
 
        /*
         * Determine cost to get just the first row of the presorted path.
@@ -496,11 +500,6 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
         * Note: cost calculation here should match
         * compare_fractional_path_costs().
         */
-       if (sorted_path->parent->rows > 1.0)
-               path_fraction = 1.0 / sorted_path->parent->rows;
-       else
-               path_fraction = 1.0;
-
        path_cost = sorted_path->startup_cost +
                path_fraction * (sorted_path->total_cost - sorted_path->startup_cost);
 
index 42a98945a38d07972ee2e8b5de16b3639196eebd..d4c586ab102e66d7d8f6d650aa4d819fce2917d0 100644 (file)
  */
 #include "postgres.h"
 
-#include "miscadmin.h"
-#include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/planmain.h"
-#include "optimizer/tlist.h"
-#include "utils/selfuncs.h"
 
 
 /*
  *       which may involve joins but not any fancier features.
  *
  * Since query_planner does not handle the toplevel processing (grouping,
- * sorting, etc) it cannot select the best path by itself.     It selects
- * two paths: the cheapest path that produces all the required tuples,
- * independent of any ordering considerations, and the cheapest path that
- * produces the expected fraction of the required tuples in the required
- * ordering, if there is a path that is cheaper for this than just sorting
- * the output of the cheapest overall path.  The caller (grouping_planner)
- * will make the final decision about which to use.
+ * sorting, etc) it cannot select the best path by itself.     Instead, it
+ * returns the RelOptInfo for the top level of joining, and the caller
+ * (grouping_planner) can choose one of the surviving paths for the rel.
+ * Normally it would choose either the rel's cheapest path, or the cheapest
+ * path for the desired sort order.
  *
- * Input parameters:
  * root describes the query to plan
  * tlist is the target list the query should produce
  *             (this is NOT necessarily root->parse->targetList!)
- * tuple_fraction is the fraction of tuples we expect will be retrieved
- * limit_tuples is a hard limit on number of tuples to retrieve,
- *             or -1 if no limit
  * qp_callback is a function to compute query_pathkeys once it's safe to do so
  * qp_extra is optional extra data to pass to qp_callback
  *
- * Output parameters:
- * *cheapest_path receives the overall-cheapest path for the query
- * *sorted_path receives the cheapest presorted path for the query,
- *                             if any (NULL if there is no useful presorted path)
- * *num_groups receives the estimated number of groups, or 1 if query
- *                             does not use grouping
- *
  * Note: the PlannerInfo node also includes a query_pathkeys field, which
  * tells query_planner the sort order that is desired in the final output
  * plan.  This value is *not* available at call time, but is computed by
  * qp_callback once we have completed merging the query's equivalence classes.
  * (We cannot construct canonical pathkeys until that's done.)
- *
- * tuple_fraction is interpreted as follows:
- *       0: expect all tuples to be retrieved (normal case)
- *       0 < tuple_fraction < 1: expect the given fraction of tuples available
- *             from the plan to be retrieved
- *       tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
- *             expected to be retrieved (ie, a LIMIT specification)
- * Note that a nonzero tuple_fraction could come from outer context; it is
- * therefore not redundant with limit_tuples.  We use limit_tuples to determine
- * whether a bounded sort can be used at runtime.
  */
-void
+RelOptInfo *
 query_planner(PlannerInfo *root, List *tlist,
-                         double tuple_fraction, double limit_tuples,
-                         query_pathkeys_callback qp_callback, void *qp_extra,
-                         Path **cheapest_path, Path **sorted_path,
-                         double *num_groups)
+                         query_pathkeys_callback qp_callback, void *qp_extra)
 {
        Query      *parse = root->parse;
        List       *joinlist;
        RelOptInfo *final_rel;
-       Path       *cheapestpath;
-       Path       *sortedpath;
        Index           rti;
        double          total_pages;
 
-       /* Make tuple_fraction, limit_tuples accessible to lower-level routines */
-       root->tuple_fraction = tuple_fraction;
-       root->limit_tuples = limit_tuples;
-
-       *num_groups = 1;                        /* default result */
-
        /*
         * If the query has an empty join tree, then it's something easy like
         * "SELECT 2+2;" or "INSERT ... VALUES()".      Fall through quickly.
         */
        if (parse->jointree->fromlist == NIL)
        {
-               /* We need a trivial path result */
-               *cheapest_path = (Path *)
-                       create_result_path((List *) parse->jointree->quals);
-               *sorted_path = NULL;
+               /* We need a dummy joinrel to describe the empty set of baserels */
+               final_rel = build_empty_join_rel(root);
+
+               /* The only path for it is a trivial Result path */
+               add_path(final_rel, (Path *)
+                                create_result_path((List *) parse->jointree->quals));
+
+               /* Select cheapest path (pretty easy in this case...) */
+               set_cheapest(final_rel);
 
                /*
                 * We still are required to call qp_callback, in case it's something
@@ -115,7 +82,8 @@ query_planner(PlannerInfo *root, List *tlist,
                 */
                root->canon_pathkeys = NIL;
                (*qp_callback) (root, qp_extra);
-               return;
+
+               return final_rel;
        }
 
        /*
@@ -259,165 +227,10 @@ query_planner(PlannerInfo *root, List *tlist,
         */
        final_rel = make_one_rel(root, joinlist);
 
+       /* Check that we got at least one usable path */
        if (!final_rel || !final_rel->cheapest_total_path ||
                final_rel->cheapest_total_path->param_info != NULL)
                elog(ERROR, "failed to construct the join relation");
 
-       /*
-        * If there's grouping going on, estimate the number of result groups. We
-        * couldn't do this any earlier because it depends on relation size
-        * estimates that were set up above.
-        *
-        * Then convert tuple_fraction to fractional form if it is absolute, and
-        * adjust it based on the knowledge that grouping_planner will be doing
-        * grouping or aggregation work with our result.
-        *
-        * This introduces some undesirable coupling between this code and
-        * grouping_planner, but the alternatives seem even uglier; we couldn't
-        * pass back completed paths without making these decisions here.
-        */
-       if (parse->groupClause)
-       {
-               List       *groupExprs;
-
-               groupExprs = get_sortgrouplist_exprs(parse->groupClause,
-                                                                                        parse->targetList);
-               *num_groups = estimate_num_groups(root,
-                                                                                 groupExprs,
-                                                                                 final_rel->rows);
-
-               /*
-                * In GROUP BY mode, an absolute LIMIT is relative to the number of
-                * groups not the number of tuples.  If the caller gave us a fraction,
-                * keep it as-is.  (In both cases, we are effectively assuming that
-                * all the groups are about the same size.)
-                */
-               if (tuple_fraction >= 1.0)
-                       tuple_fraction /= *num_groups;
-
-               /*
-                * If both GROUP BY and ORDER BY are specified, we will need two
-                * levels of sort --- and, therefore, certainly need to read all the
-                * tuples --- unless ORDER BY is a subset of GROUP BY.  Likewise if we
-                * have both DISTINCT and GROUP BY, or if we have a window
-                * specification not compatible with the GROUP BY.
-                */
-               if (!pathkeys_contained_in(root->sort_pathkeys, root->group_pathkeys) ||
-                       !pathkeys_contained_in(root->distinct_pathkeys, root->group_pathkeys) ||
-                !pathkeys_contained_in(root->window_pathkeys, root->group_pathkeys))
-                       tuple_fraction = 0.0;
-
-               /* In any case, limit_tuples shouldn't be specified here */
-               Assert(limit_tuples < 0);
-       }
-       else if (parse->hasAggs || root->hasHavingQual)
-       {
-               /*
-                * Ungrouped aggregate will certainly want to read all the tuples, and
-                * it will deliver a single result row (so leave *num_groups 1).
-                */
-               tuple_fraction = 0.0;
-
-               /* limit_tuples shouldn't be specified here */
-               Assert(limit_tuples < 0);
-       }
-       else if (parse->distinctClause)
-       {
-               /*
-                * Since there was no grouping or aggregation, it's reasonable to
-                * assume the UNIQUE filter has effects comparable to GROUP BY. Return
-                * the estimated number of output rows for use by caller. (If DISTINCT
-                * is used with grouping, we ignore its effects for rowcount
-                * estimation purposes; this amounts to assuming the grouped rows are
-                * distinct already.)
-                */
-               List       *distinctExprs;
-
-               distinctExprs = get_sortgrouplist_exprs(parse->distinctClause,
-                                                                                               parse->targetList);
-               *num_groups = estimate_num_groups(root,
-                                                                                 distinctExprs,
-                                                                                 final_rel->rows);
-
-               /*
-                * Adjust tuple_fraction the same way as for GROUP BY, too.
-                */
-               if (tuple_fraction >= 1.0)
-                       tuple_fraction /= *num_groups;
-
-               /* limit_tuples shouldn't be specified here */
-               Assert(limit_tuples < 0);
-       }
-       else
-       {
-               /*
-                * Plain non-grouped, non-aggregated query: an absolute tuple fraction
-                * can be divided by the number of tuples.
-                */
-               if (tuple_fraction >= 1.0)
-                       tuple_fraction /= final_rel->rows;
-       }
-
-       /*
-        * Pick out the cheapest-total path and the cheapest presorted path for
-        * the requested pathkeys (if there is one).  We should take the tuple
-        * fraction into account when selecting the cheapest presorted path, but
-        * not when selecting the cheapest-total path, since if we have to sort
-        * then we'll have to fetch all the tuples.  (But there's a special case:
-        * if query_pathkeys is NIL, meaning order doesn't matter, then the
-        * "cheapest presorted" path will be the cheapest overall for the tuple
-        * fraction.)
-        *
-        * The cheapest-total path is also the one to use if grouping_planner
-        * decides to use hashed aggregation, so we return it separately even if
-        * this routine thinks the presorted path is the winner.
-        */
-       cheapestpath = final_rel->cheapest_total_path;
-
-       sortedpath =
-               get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist,
-                                                                                                 root->query_pathkeys,
-                                                                                                 NULL,
-                                                                                                 tuple_fraction);
-
-       /* Don't return same path in both guises; just wastes effort */
-       if (sortedpath == cheapestpath)
-               sortedpath = NULL;
-
-       /*
-        * Forget about the presorted path if it would be cheaper to sort the
-        * cheapest-total path.  Here we need consider only the behavior at the
-        * tuple fraction point.
-        */
-       if (sortedpath)
-       {
-               Path            sort_path;      /* dummy for result of cost_sort */
-
-               if (root->query_pathkeys == NIL ||
-                       pathkeys_contained_in(root->query_pathkeys,
-                                                                 cheapestpath->pathkeys))
-               {
-                       /* No sort needed for cheapest path */
-                       sort_path.startup_cost = cheapestpath->startup_cost;
-                       sort_path.total_cost = cheapestpath->total_cost;
-               }
-               else
-               {
-                       /* Figure cost for sorting */
-                       cost_sort(&sort_path, root, root->query_pathkeys,
-                                         cheapestpath->total_cost,
-                                         final_rel->rows, final_rel->width,
-                                         0.0, work_mem, limit_tuples);
-               }
-
-               if (compare_fractional_path_costs(sortedpath, &sort_path,
-                                                                                 tuple_fraction) > 0)
-               {
-                       /* Presorted path is a loser */
-                       sortedpath = NULL;
-               }
-       }
-
-       *cheapest_path = cheapestpath;
-       *sorted_path = sortedpath;
+       return final_rel;
 }
index 49bd9302fa5c1e8467d0b0617773f103e3502a0d..bcc0d451f0d48a5203704769ebb90e2be53d3231 100644 (file)
@@ -39,6 +39,7 @@
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/rel.h"
+#include "utils/selfuncs.h"
 
 
 /* GUC parameter */
@@ -1125,10 +1126,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
        {
                /* No set operations, do regular planning */
                List       *sub_tlist;
-               double          sub_limit_tuples;
                AttrNumber *groupColIdx = NULL;
                bool            need_tlist_eval = true;
                standard_qp_extra qp_extra;
+               RelOptInfo *final_rel;
                Path       *cheapest_path;
                Path       *sorted_path;
                Path       *best_path;
@@ -1204,6 +1205,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                        preprocess_minmax_aggregates(root, tlist);
                }
 
+               /* Make tuple_fraction accessible to lower-level routines */
+               root->tuple_fraction = tuple_fraction;
+
                /*
                 * Figure out whether there's a hard limit on the number of rows that
                 * query_planner's result subplan needs to return.  Even if we know a
@@ -1215,9 +1219,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                        parse->hasAggs ||
                        parse->hasWindowFuncs ||
                        root->hasHavingQual)
-                       sub_limit_tuples = -1.0;
+                       root->limit_tuples = -1.0;
                else
-                       sub_limit_tuples = limit_tuples;
+                       root->limit_tuples = limit_tuples;
 
                /* Set up data needed by standard_qp_callback */
                qp_extra.tlist = tlist;
@@ -1225,31 +1229,164 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 
                /*
                 * Generate the best unsorted and presorted paths for this Query (but
-                * note there may not be any presorted path).  We also generate (in
+                * note there may not be any presorted paths).  We also generate (in
                 * standard_qp_callback) pathkey representations of the query's sort
-                * clause, distinct clause, etc.  query_planner will also estimate the
-                * number of groups in the query.
+                * clause, distinct clause, etc.
                 */
-               query_planner(root, sub_tlist, tuple_fraction, sub_limit_tuples,
-                                         standard_qp_callback, &qp_extra,
-                                         &cheapest_path, &sorted_path, &dNumGroups);
+               final_rel = query_planner(root, sub_tlist,
+                                                                 standard_qp_callback, &qp_extra);
 
                /*
-                * Extract rowcount and width estimates for possible use in grouping
-                * decisions.  Beware here of the possibility that
-                * cheapest_path->parent is NULL (ie, there is no FROM clause).
+                * Extract rowcount and width estimates for use below.
                 */
-               if (cheapest_path->parent)
+               path_rows = final_rel->rows;
+               path_width = final_rel->width;
+
+               /*
+                * If there's grouping going on, estimate the number of result groups.
+                * We couldn't do this any earlier because it depends on relation size
+                * estimates that are created within query_planner().
+                *
+                * Then convert tuple_fraction to fractional form if it is absolute,
+                * and if grouping or aggregation is involved, adjust tuple_fraction
+                * to describe the fraction of the underlying un-aggregated tuples
+                * that will be fetched.
+                */
+               dNumGroups = 1;                 /* in case not grouping */
+
+               if (parse->groupClause)
+               {
+                       List       *groupExprs;
+
+                       groupExprs = get_sortgrouplist_exprs(parse->groupClause,
+                                                                                                parse->targetList);
+                       dNumGroups = estimate_num_groups(root, groupExprs, path_rows);
+
+                       /*
+                        * In GROUP BY mode, an absolute LIMIT is relative to the number
+                        * of groups not the number of tuples.  If the caller gave us a
+                        * fraction, keep it as-is.  (In both cases, we are effectively
+                        * assuming that all the groups are about the same size.)
+                        */
+                       if (tuple_fraction >= 1.0)
+                               tuple_fraction /= dNumGroups;
+
+                       /*
+                        * If both GROUP BY and ORDER BY are specified, we will need two
+                        * levels of sort --- and, therefore, certainly need to read all
+                        * the tuples --- unless ORDER BY is a subset of GROUP BY.
+                        * Likewise if we have both DISTINCT and GROUP BY, or if we have a
+                        * window specification not compatible with the GROUP BY.
+                        */
+                       if (!pathkeys_contained_in(root->sort_pathkeys,
+                                                                          root->group_pathkeys) ||
+                               !pathkeys_contained_in(root->distinct_pathkeys,
+                                                                          root->group_pathkeys) ||
+                               !pathkeys_contained_in(root->window_pathkeys,
+                                                                          root->group_pathkeys))
+                               tuple_fraction = 0.0;
+               }
+               else if (parse->hasAggs || root->hasHavingQual)
                {
-                       path_rows = cheapest_path->parent->rows;
-                       path_width = cheapest_path->parent->width;
+                       /*
+                        * Ungrouped aggregate will certainly want to read all the tuples,
+                        * and it will deliver a single result row (so leave dNumGroups
+                        * set to 1).
+                        */
+                       tuple_fraction = 0.0;
+               }
+               else if (parse->distinctClause)
+               {
+                       /*
+                        * Since there was no grouping or aggregation, it's reasonable to
+                        * assume the UNIQUE filter has effects comparable to GROUP BY.
+                        * (If DISTINCT is used with grouping, we ignore its effects for
+                        * rowcount estimation purposes; this amounts to assuming the
+                        * grouped rows are distinct already.)
+                        */
+                       List       *distinctExprs;
+
+                       distinctExprs = get_sortgrouplist_exprs(parse->distinctClause,
+                                                                                                       parse->targetList);
+                       dNumGroups = estimate_num_groups(root, distinctExprs, path_rows);
+
+                       /*
+                        * Adjust tuple_fraction the same way as for GROUP BY, too.
+                        */
+                       if (tuple_fraction >= 1.0)
+                               tuple_fraction /= dNumGroups;
                }
                else
                {
-                       path_rows = 1;          /* assume non-set result */
-                       path_width = 100;       /* arbitrary */
+                       /*
+                        * Plain non-grouped, non-aggregated query: an absolute tuple
+                        * fraction can be divided by the number of tuples.
+                        */
+                       if (tuple_fraction >= 1.0)
+                               tuple_fraction /= path_rows;
+               }
+
+               /*
+                * Pick out the cheapest-total path as well as the cheapest presorted
+                * path for the requested pathkeys (if there is one).  We should take
+                * the tuple fraction into account when selecting the cheapest
+                * presorted path, but not when selecting the cheapest-total path,
+                * since if we have to sort then we'll have to fetch all the tuples.
+                * (But there's a special case: if query_pathkeys is NIL, meaning
+                * order doesn't matter, then the "cheapest presorted" path will be
+                * the cheapest overall for the tuple fraction.)
+                */
+               cheapest_path = final_rel->cheapest_total_path;
+
+               sorted_path =
+                       get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist,
+                                                                                                         root->query_pathkeys,
+                                                                                                         NULL,
+                                                                                                         tuple_fraction);
+
+               /* Don't consider same path in both guises; just wastes effort */
+               if (sorted_path == cheapest_path)
+                       sorted_path = NULL;
+
+               /*
+                * Forget about the presorted path if it would be cheaper to sort the
+                * cheapest-total path.  Here we need consider only the behavior at
+                * the tuple_fraction point.  Also, limit_tuples is only relevant if
+                * not grouping/aggregating, so use root->limit_tuples in the
+                * cost_sort call.
+                */
+               if (sorted_path)
+               {
+                       Path            sort_path;              /* dummy for result of cost_sort */
+
+                       if (root->query_pathkeys == NIL ||
+                               pathkeys_contained_in(root->query_pathkeys,
+                                                                         cheapest_path->pathkeys))
+                       {
+                               /* No sort needed for cheapest path */
+                               sort_path.startup_cost = cheapest_path->startup_cost;
+                               sort_path.total_cost = cheapest_path->total_cost;
+                       }
+                       else
+                       {
+                               /* Figure cost for sorting */
+                               cost_sort(&sort_path, root, root->query_pathkeys,
+                                                 cheapest_path->total_cost,
+                                                 path_rows, path_width,
+                                                 0.0, work_mem, root->limit_tuples);
+                       }
+
+                       if (compare_fractional_path_costs(sorted_path, &sort_path,
+                                                                                         tuple_fraction) > 0)
+                       {
+                               /* Presorted path is a loser */
+                               sorted_path = NULL;
+                       }
                }
 
+               /*
+                * Consider whether we want to use hashing instead of sorting.
+                */
                if (parse->groupClause)
                {
                        /*
@@ -1288,7 +1425,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                /*
                 * Select the best path.  If we are doing hashed grouping, we will
                 * always read all the input tuples, so use the cheapest-total path.
-                * Otherwise, trust query_planner's decision about which to use.
+                * Otherwise, the comparison above is correct.
                 */
                if (use_hashed_grouping || use_hashed_distinct || !sorted_path)
                        best_path = cheapest_path;
@@ -1658,7 +1795,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                 * If there was grouping or aggregation, use the current number of
                 * rows as the estimated number of DISTINCT rows (ie, assume the
                 * result was already mostly unique).  If not, use the number of
-                * distinct-groups calculated by query_planner.
+                * distinct-groups calculated previously.
                 */
                if (parse->groupClause || root->hasHavingQual || parse->hasAggs)
                        dNumDistinctRows = result_plan->plan_rows;
@@ -2576,8 +2713,8 @@ choose_hashed_grouping(PlannerInfo *root,
         * We need to consider cheapest_path + hashagg [+ final sort] versus
         * either cheapest_path [+ sort] + group or agg [+ final sort] or
         * presorted_path + group or agg [+ final sort] where brackets indicate a
-        * step that may not be needed. We assume query_planner() will have
-        * returned a presorted path only if it's a winner compared to
+        * step that may not be needed.  We assume grouping_planner() will have
+        * passed us a presorted path only if it's a winner compared to
         * cheapest_path for this purpose.
         *
         * These path variables are dummies that just hold cost fields; we don't
@@ -2630,12 +2767,8 @@ choose_hashed_grouping(PlannerInfo *root,
                                  0.0, work_mem, limit_tuples);
 
        /*
-        * Now make the decision using the top-level tuple fraction.  First we
-        * have to convert an absolute count (LIMIT) into fractional form.
+        * Now make the decision using the top-level tuple fraction.
         */
-       if (tuple_fraction >= 1.0)
-               tuple_fraction /= dNumGroups;
-
        if (compare_fractional_path_costs(&hashed_p, &sorted_p,
                                                                          tuple_fraction) < 0)
        {
@@ -2781,12 +2914,8 @@ choose_hashed_distinct(PlannerInfo *root,
                                  0.0, work_mem, limit_tuples);
 
        /*
-        * Now make the decision using the top-level tuple fraction.  First we
-        * have to convert an absolute count (LIMIT) into fractional form.
+        * Now make the decision using the top-level tuple fraction.
         */
-       if (tuple_fraction >= 1.0)
-               tuple_fraction /= dNumDistinctRows;
-
        if (compare_fractional_path_costs(&hashed_p, &sorted_p,
                                                                          tuple_fraction) < 0)
        {
index 52842931ec5552c04fdec41e013c36e1897fd4e4..afeb6ddb8c446dfa5e01d7d603b16aaa7a4163b5 100644 (file)
@@ -1333,15 +1333,16 @@ is_simple_subquery(Query *subquery, RangeTblEntry *rte,
                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.  It would pose a
-        * problem for the PlaceHolderVar mechanism too, since we'd have no way to
-        * identify where to evaluate a PHV coming out of the subquery. Not worth
-        * working hard on this, just to collapse SubqueryScan/Result into Result;
-        * especially since the SubqueryScan can often be optimized away by
-        * setrefs.c anyway.
+        * Don't pull up a subquery with an empty jointree.  query_planner() will
+        * correctly generate a Result plan for a jointree that's totally empty,
+        * but we can't cope with an empty FromExpr appearing lower down in a
+        * jointree: we identify join rels via baserelid sets, so we couldn't
+        * distinguish a join containing such a FromExpr from one without it.
+        * This would for example break the PlaceHolderVar mechanism, since we'd
+        * have no way to identify where to evaluate a PHV coming out of the
+        * subquery.  Not worth working hard on this, just to collapse
+        * SubqueryScan/Result into Result; especially since the SubqueryScan can
+        * often be optimized away by setrefs.c anyway.
         */
        if (subquery->jointree->fromlist == NIL)
                return false;
index 8ee5671a551912a9bbc1020971b2fb9a3b5fadf1..1f198201c32fa9d11123bf313461a12a9ed5e35a 100644 (file)
@@ -676,6 +676,36 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
 }
 
 
+/*
+ * build_empty_join_rel
+ *             Build a dummy join relation describing an empty set of base rels.
+ *
+ * This is used for queries with empty FROM clauses, such as "SELECT 2+2" or
+ * "INSERT INTO foo VALUES(...)".  We don't try very hard to make the empty
+ * joinrel completely valid, since no real planning will be done with it ---
+ * we just need it to carry a simple Result path out of query_planner().
+ */
+RelOptInfo *
+build_empty_join_rel(PlannerInfo *root)
+{
+       RelOptInfo *joinrel;
+
+       /* The dummy join relation should be the only one ... */
+       Assert(root->join_rel_list == NIL);
+
+       joinrel = makeNode(RelOptInfo);
+       joinrel->reloptkind = RELOPT_JOINREL;
+       joinrel->relids = NULL;         /* empty set */
+       joinrel->rows = 1;                      /* we produce one row for such cases */
+       joinrel->width = 0;                     /* it contains no Vars */
+       joinrel->rtekind = RTE_JOIN;
+
+       root->join_rel_list = lappend(root->join_rel_list, joinrel);
+
+       return joinrel;
+}
+
+
 /*
  * find_childrel_appendrelinfo
  *             Get the AppendRelInfo associated with an appendrel child rel.
index bc687895bfa1429271c996a96dfc463305833c56..9686229134e228be1187d6c727034abb5d186ffe 100644 (file)
@@ -142,6 +142,7 @@ extern RelOptInfo *build_join_rel(PlannerInfo *root,
                           RelOptInfo *inner_rel,
                           SpecialJoinInfo *sjinfo,
                           List **restrictlist_ptr);
+extern RelOptInfo *build_empty_join_rel(PlannerInfo *root);
 extern AppendRelInfo *find_childrel_appendrelinfo(PlannerInfo *root,
                                                        RelOptInfo *rel);
 extern ParamPathInfo *get_baserel_parampathinfo(PlannerInfo *root,
index bd6841f6cdbdd07d477d3351ac8d10f86cd35fd5..ba7ae7c36ab62039d3ff6d422ed5126e8fa077b6 100644 (file)
@@ -27,11 +27,8 @@ typedef void (*query_pathkeys_callback) (PlannerInfo *root, void *extra);
 /*
  * prototypes for plan/planmain.c
  */
-extern void query_planner(PlannerInfo *root, List *tlist,
-                         double tuple_fraction, double limit_tuples,
-                         query_pathkeys_callback qp_callback, void *qp_extra,
-                         Path **cheapest_path, Path **sorted_path,
-                         double *num_groups);
+extern RelOptInfo *query_planner(PlannerInfo *root, List *tlist,
+                         query_pathkeys_callback qp_callback, void *qp_extra);
 
 /*
  * prototypes for plan/planagg.c