Add a new upper planner relation for partially-aggregated results.
authorRobert Haas <[email protected]>
Mon, 26 Feb 2018 14:30:12 +0000 (09:30 -0500)
committerRobert Haas <[email protected]>
Mon, 26 Feb 2018 14:32:32 +0000 (09:32 -0500)
Up until now, we've abused grouped_rel->partial_pathlist as a place to
store partial paths that have been partially aggregate, but that's
really not correct, because a partial path for a relation is supposed
to be one which produces the correct results with the addition of only
a Gather or Gather Merge node, and these paths also require a Finalize
Aggregate step.  Instead, add a new partially_group_rel which can hold
either partial paths (which need to be gathered and then have
aggregation finalized) or non-partial paths (which only need to have
aggregation finalized).  This allows us to reuse generate_gather_paths
for partially_grouped_rel instead of writing new code, so that this
patch actually basically no net new code while making things cleaner,
simplifying things for pending patches for partition-wise aggregate.

Robert Haas and Jeevan Chalke.  The larger patch series of which this
patch is a part was also reviewed and tested by Antonin Houska,
Rajkumar Raghuwanshi, David Rowley, Dilip Kumar, Konstantin Knizhnik,
Pascal Legrand, Rafia Sabih, and me.

Discussion: http://postgr.es/m/CA+TgmobrzFYS3+U8a_BCy3-hOvh5UyJbC18rEcYehxhpw5=ETA@mail.gmail.com
Discussion: http://postgr.es/m/CA+TgmoZyQEjdBNuoG9-wC5GQ5GrO4544Myo13dVptvx+uLg9uQ@mail.gmail.com

src/backend/optimizer/README
src/backend/optimizer/geqo/geqo_eval.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/planner.c
src/include/nodes/relation.h
src/include/optimizer/paths.h

index 84e60f7f6f04e77f8a354ad061838885a8298849..3e254c8b2d839d3ccacb4de65e3cf5c4bea5cf5a 100644 (file)
@@ -998,6 +998,7 @@ considered useful for each step.  Currently, we may create these types of
 additional RelOptInfos during upper-level planning:
 
 UPPERREL_SETOP         result of UNION/INTERSECT/EXCEPT, if any
+UPPERREL_PARTIAL_GROUP_AGG     result of partial grouping/aggregation, if any
 UPPERREL_GROUP_AGG     result of grouping/aggregation, if any
 UPPERREL_WINDOW                result of window functions, if any
 UPPERREL_DISTINCT      result of "SELECT DISTINCT", if any
index 57f0f594e5bec9696d5073cd727072d11d9649fa..0be2a73e05b7dd52ae23a053a5318bfbc5620a99 100644 (file)
@@ -268,7 +268,7 @@ merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, bool force)
                                generate_partitionwise_join_paths(root, joinrel);
 
                                /* Create GatherPaths for any useful partial paths for rel */
-                               generate_gather_paths(root, joinrel);
+                               generate_gather_paths(root, joinrel, false);
 
                                /* Find and save the cheapest paths for this joinrel */
                                set_cheapest(joinrel);
index f714247ebb0d39bb9b7bb753d0f8fa514e26e29a..1c792a00eb2bf57004f6d0361732d7ca44970465 100644 (file)
@@ -488,7 +488,7 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
         * we'll consider gathering partial paths for the parent appendrel.)
         */
        if (rel->reloptkind == RELOPT_BASEREL)
-               generate_gather_paths(root, rel);
+               generate_gather_paths(root, rel, false);
 
        /*
         * Allow a plugin to editorialize on the set of Paths for this base
@@ -2444,27 +2444,42 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  * This must not be called until after we're done creating all partial paths
  * for the specified relation.  (Otherwise, add_partial_path might delete a
  * path that some GatherPath or GatherMergePath has a reference to.)
+ *
+ * If we're generating paths for a scan or join relation, override_rows will
+ * be false, and we'll just use the relation's size estimate.  When we're
+ * being called for a partially-grouped path, though, we need to override
+ * the rowcount estimate.  (It's not clear that the particular value we're
+ * using here is actually best, but the underlying rel has no estimate so
+ * we must do something.)
  */
 void
-generate_gather_paths(PlannerInfo *root, RelOptInfo *rel)
+generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_rows)
 {
        Path       *cheapest_partial_path;
        Path       *simple_gather_path;
        ListCell   *lc;
+       double          rows;
+       double     *rowsp = NULL;
 
        /* If there are no partial paths, there's nothing to do here. */
        if (rel->partial_pathlist == NIL)
                return;
 
+       /* Should we override the rel's rowcount estimate? */
+       if (override_rows)
+               rowsp = &rows;
+
        /*
         * The output of Gather is always unsorted, so there's only one partial
         * path of interest: the cheapest one.  That will be the one at the front
         * of partial_pathlist because of the way add_partial_path works.
         */
        cheapest_partial_path = linitial(rel->partial_pathlist);
+       rows =
+               cheapest_partial_path->rows * cheapest_partial_path->parallel_workers;
        simple_gather_path = (Path *)
                create_gather_path(root, rel, cheapest_partial_path, rel->reltarget,
-                                                  NULL, NULL);
+                                                  NULL, rowsp);
        add_path(rel, simple_gather_path);
 
        /*
@@ -2479,8 +2494,9 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel)
                if (subpath->pathkeys == NIL)
                        continue;
 
+               rows = subpath->rows * subpath->parallel_workers;
                path = create_gather_merge_path(root, rel, subpath, rel->reltarget,
-                                                                               subpath->pathkeys, NULL, NULL);
+                                                                               subpath->pathkeys, NULL, rowsp);
                add_path(rel, &path->path);
        }
 }
@@ -2653,7 +2669,7 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
                        generate_partitionwise_join_paths(root, rel);
 
                        /* Create GatherPaths for any useful partial paths for rel */
-                       generate_gather_paths(root, rel);
+                       generate_gather_paths(root, rel, false);
 
                        /* Find and save the cheapest paths for this rel */
                        set_cheapest(rel);
index 3e8cd1447cc18203f9385327d2d9e1b3d10e8296..e8f6cc559b34d37499b8d5a7b677cfc99ae375ce 100644 (file)
@@ -186,19 +186,17 @@ static PathTarget *make_sort_input_target(PlannerInfo *root,
 static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel,
                                          List *targets, List *targets_contain_srfs);
 static void add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
-                                                 RelOptInfo *grouped_rel, PathTarget *target,
-                                                 PathTarget *partial_grouping_target,
+                                                 RelOptInfo *grouped_rel,
+                                                 PathTarget *target,
+                                                 RelOptInfo *partially_grouped_rel,
                                                  const AggClauseCosts *agg_costs,
                                                  const AggClauseCosts *agg_final_costs,
                                                  grouping_sets_data *gd, bool can_sort, bool can_hash,
                                                  double dNumGroups, List *havingQual);
-static void add_partial_paths_to_grouping_rel(PlannerInfo *root,
+static void add_paths_to_partial_grouping_rel(PlannerInfo *root,
                                                                  RelOptInfo *input_rel,
-                                                                 RelOptInfo *grouped_rel,
-                                                                 PathTarget *target,
-                                                                 PathTarget *partial_grouping_target,
+                                                                 RelOptInfo *partial_grouped_rel,
                                                                  AggClauseCosts *agg_partial_costs,
-                                                                 AggClauseCosts *agg_final_costs,
                                                                  grouping_sets_data *gd,
                                                                  bool can_sort,
                                                                  bool can_hash,
@@ -3601,6 +3599,11 @@ estimate_hashagg_tablesize(Path *path, const AggClauseCosts *agg_costs,
  * create_grouping_paths
  *
  * Build a new upperrel containing Paths for grouping and/or aggregation.
+ * Along the way, we also build an upperrel for Paths which are partially
+ * grouped and/or aggregated.  A partially grouped and/or aggregated path
+ * needs a FinalizeAggregate node to complete the aggregation.  Currently,
+ * the only partially grouped paths we build are also partial paths; that
+ * is, they need a Gather and then a FinalizeAggregate.
  *
  * input_rel: contains the source-data Paths
  * target: the pathtarget for the result Paths to compute
@@ -3627,7 +3630,7 @@ create_grouping_paths(PlannerInfo *root,
        Query      *parse = root->parse;
        Path       *cheapest_path = input_rel->cheapest_total_path;
        RelOptInfo *grouped_rel;
-       PathTarget *partial_grouping_target = NULL;
+       RelOptInfo *partially_grouped_rel;
        AggClauseCosts agg_partial_costs;       /* parallel only */
        AggClauseCosts agg_final_costs; /* parallel only */
        double          dNumGroups;
@@ -3635,26 +3638,41 @@ create_grouping_paths(PlannerInfo *root,
        bool            can_sort;
        bool            try_parallel_aggregation;
 
-       /* For now, do all work in the (GROUP_AGG, NULL) upperrel */
+       /*
+        * For now, all aggregated paths are added to the (GROUP_AGG, NULL)
+        * upperrel.  Paths that are only partially aggregated go into the
+        * (UPPERREL_PARTIAL_GROUP_AGG, NULL) upperrel.
+        */
        grouped_rel = fetch_upper_rel(root, UPPERREL_GROUP_AGG, NULL);
+       partially_grouped_rel = fetch_upper_rel(root, UPPERREL_PARTIAL_GROUP_AGG,
+                                                                                       NULL);
 
        /*
         * If the input relation is not parallel-safe, then the grouped relation
         * can't be parallel-safe, either.  Otherwise, it's parallel-safe if the
-        * target list and HAVING quals are parallel-safe.
+        * target list and HAVING quals are parallel-safe.  The partially grouped
+        * relation obeys the same rules.
         */
        if (input_rel->consider_parallel &&
                is_parallel_safe(root, (Node *) target->exprs) &&
                is_parallel_safe(root, (Node *) parse->havingQual))
+       {
                grouped_rel->consider_parallel = true;
+               partially_grouped_rel->consider_parallel = true;
+       }
 
        /*
-        * If the input rel belongs to a single FDW, so does the grouped rel.
+        * If the input rel belongs to a single FDW, so does the grouped rel. Same
+        * for the partially_grouped_rel.
         */
        grouped_rel->serverid = input_rel->serverid;
        grouped_rel->userid = input_rel->userid;
        grouped_rel->useridiscurrent = input_rel->useridiscurrent;
        grouped_rel->fdwroutine = input_rel->fdwroutine;
+       partially_grouped_rel->serverid = input_rel->serverid;
+       partially_grouped_rel->userid = input_rel->userid;
+       partially_grouped_rel->useridiscurrent = input_rel->useridiscurrent;
+       partially_grouped_rel->fdwroutine = input_rel->fdwroutine;
 
        /*
         * Check for degenerate grouping.
@@ -3778,14 +3796,13 @@ create_grouping_paths(PlannerInfo *root,
 
        /*
         * Before generating paths for grouped_rel, we first generate any possible
-        * partial paths; that way, later code can easily consider both parallel
-        * and non-parallel approaches to grouping.  Note that the partial paths
-        * we generate here are also partially aggregated, so simply pushing a
-        * Gather node on top is insufficient to create a final path, as would be
-        * the case for a scan/join rel.
+        * partial paths for partially_grouped_rel; that way, later code can
+        * easily consider both parallel and non-parallel approaches to grouping.
         */
        if (try_parallel_aggregation)
        {
+               PathTarget *partial_grouping_target;
+
                /*
                 * Build target list for partial aggregate paths.  These paths cannot
                 * just emit the same tlist as regular aggregate paths, because (1) we
@@ -3794,6 +3811,7 @@ create_grouping_paths(PlannerInfo *root,
                 * partial mode.
                 */
                partial_grouping_target = make_partial_grouping_target(root, target);
+               partially_grouped_rel->reltarget = partial_grouping_target;
 
                /*
                 * Collect statistics about aggregates for estimating costs of
@@ -3817,16 +3835,16 @@ create_grouping_paths(PlannerInfo *root,
                                                                 &agg_final_costs);
                }
 
-               add_partial_paths_to_grouping_rel(root, input_rel, grouped_rel, target,
-                                                                                 partial_grouping_target,
-                                                                                 &agg_partial_costs, &agg_final_costs,
+               add_paths_to_partial_grouping_rel(root, input_rel,
+                                                                                 partially_grouped_rel,
+                                                                                 &agg_partial_costs,
                                                                                  gd, can_sort, can_hash,
                                                                                  (List *) parse->havingQual);
        }
 
        /* Build final grouping paths */
        add_paths_to_grouping_rel(root, input_rel, grouped_rel, target,
-                                                         partial_grouping_target, agg_costs,
+                                                         partially_grouped_rel, agg_costs,
                                                          &agg_final_costs, gd, can_sort, can_hash,
                                                          dNumGroups, (List *) parse->havingQual);
 
@@ -3854,16 +3872,6 @@ create_grouping_paths(PlannerInfo *root,
        /* Now choose the best path(s) */
        set_cheapest(grouped_rel);
 
-       /*
-        * We've been using the partial pathlist for the grouped relation to hold
-        * partially aggregated paths, but that's actually a little bit bogus
-        * because it's unsafe for later planning stages -- like ordered_rel ---
-        * to get the idea that they can use these partial paths as if they didn't
-        * need a FinalizeAggregate step.  Zap the partial pathlist at this stage
-        * so we don't get confused.
-        */
-       grouped_rel->partial_pathlist = NIL;
-
        return grouped_rel;
 }
 
@@ -5996,8 +6004,9 @@ get_partitioned_child_rels_for_join(PlannerInfo *root, Relids join_relids)
  */
 static void
 add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
-                                                 RelOptInfo *grouped_rel, PathTarget *target,
-                                                 PathTarget *partial_grouping_target,
+                                                 RelOptInfo *grouped_rel,
+                                                 PathTarget *target,
+                                                 RelOptInfo *partially_grouped_rel,
                                                  const AggClauseCosts *agg_costs,
                                                  const AggClauseCosts *agg_final_costs,
                                                  grouping_sets_data *gd, bool can_sort, bool can_hash,
@@ -6079,32 +6088,27 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
                }
 
                /*
-                * Now generate a complete GroupAgg Path atop of the cheapest partial
-                * path.  We can do this using either Gather or Gather Merge.
+                * Instead of operating directly on the input relation, we can
+                * consider finalizing a partially aggregated path.
                 */
-               if (grouped_rel->partial_pathlist)
+               foreach(lc, partially_grouped_rel->pathlist)
                {
-                       Path       *path = (Path *) linitial(grouped_rel->partial_pathlist);
-                       double          total_groups = path->rows * path->parallel_workers;
-
-                       path = (Path *) create_gather_path(root,
-                                                                                          grouped_rel,
-                                                                                          path,
-                                                                                          partial_grouping_target,
-                                                                                          NULL,
-                                                                                          &total_groups);
+                       Path       *path = (Path *) lfirst(lc);
 
                        /*
-                        * Since Gather's output is always unsorted, we'll need to sort,
-                        * unless there's no GROUP BY clause or a degenerate (constant)
-                        * one, in which case there will only be a single group.
+                        * Insert a Sort node, if required.  But there's no point in
+                        * sorting anything but the cheapest path.
                         */
-                       if (root->group_pathkeys)
+                       if (!pathkeys_contained_in(root->group_pathkeys, path->pathkeys))
+                       {
+                               if (path != partially_grouped_rel->cheapest_total_path)
+                                       continue;
                                path = (Path *) create_sort_path(root,
                                                                                                 grouped_rel,
                                                                                                 path,
                                                                                                 root->group_pathkeys,
                                                                                                 -1.0);
+                       }
 
                        if (parse->hasAggs)
                                add_path(grouped_rel, (Path *)
@@ -6127,70 +6131,6 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
                                                                                   parse->groupClause,
                                                                                   havingQual,
                                                                                   dNumGroups));
-
-                       /*
-                        * The point of using Gather Merge rather than Gather is that it
-                        * can preserve the ordering of the input path, so there's no
-                        * reason to try it unless (1) it's possible to produce more than
-                        * one output row and (2) we want the output path to be ordered.
-                        */
-                       if (parse->groupClause != NIL && root->group_pathkeys != NIL)
-                       {
-                               foreach(lc, grouped_rel->partial_pathlist)
-                               {
-                                       Path       *subpath = (Path *) lfirst(lc);
-                                       Path       *gmpath;
-                                       double          total_groups;
-
-                                       /*
-                                        * It's useful to consider paths that are already properly
-                                        * ordered for Gather Merge, because those don't need a
-                                        * sort. It's also useful to consider the cheapest path,
-                                        * because sorting it in parallel and then doing Gather
-                                        * Merge may be better than doing an unordered Gather
-                                        * followed by a sort. But there's no point in considering
-                                        * non-cheapest paths that aren't already sorted
-                                        * correctly.
-                                        */
-                                       if (path != subpath &&
-                                               !pathkeys_contained_in(root->group_pathkeys,
-                                                                                          subpath->pathkeys))
-                                               continue;
-
-                                       total_groups = subpath->rows * subpath->parallel_workers;
-
-                                       gmpath = (Path *)
-                                               create_gather_merge_path(root,
-                                                                                                grouped_rel,
-                                                                                                subpath,
-                                                                                                partial_grouping_target,
-                                                                                                root->group_pathkeys,
-                                                                                                NULL,
-                                                                                                &total_groups);
-
-                                       if (parse->hasAggs)
-                                               add_path(grouped_rel, (Path *)
-                                                                create_agg_path(root,
-                                                                                                grouped_rel,
-                                                                                                gmpath,
-                                                                                                target,
-                                                                                                parse->groupClause ? AGG_SORTED : AGG_PLAIN,
-                                                                                                AGGSPLIT_FINAL_DESERIAL,
-                                                                                                parse->groupClause,
-                                                                                                havingQual,
-                                                                                                agg_final_costs,
-                                                                                                dNumGroups));
-                                       else
-                                               add_path(grouped_rel, (Path *)
-                                                                create_group_path(root,
-                                                                                                  grouped_rel,
-                                                                                                  gmpath,
-                                                                                                  target,
-                                                                                                  parse->groupClause,
-                                                                                                  havingQual,
-                                                                                                  dNumGroups));
-                               }
-                       }
                }
        }
 
@@ -6240,29 +6180,19 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
                }
 
                /*
-                * Generate a HashAgg Path atop of the cheapest partial path. Once
-                * again, we'll only do this if it looks as though the hash table
-                * won't exceed work_mem.
+                * Generate a Finalize HashAgg Path atop of the cheapest partially
+                * grouped path. Once again, we'll only do this if it looks as though
+                * the hash table won't exceed work_mem.
                 */
-               if (grouped_rel->partial_pathlist)
+               if (partially_grouped_rel->pathlist)
                {
-                       Path       *path = (Path *) linitial(grouped_rel->partial_pathlist);
+                       Path       *path = partially_grouped_rel->cheapest_total_path;
 
                        hashaggtablesize = estimate_hashagg_tablesize(path,
                                                                                                                  agg_final_costs,
                                                                                                                  dNumGroups);
 
                        if (hashaggtablesize < work_mem * 1024L)
-                       {
-                               double          total_groups = path->rows * path->parallel_workers;
-
-                               path = (Path *) create_gather_path(root,
-                                                                                                  grouped_rel,
-                                                                                                  path,
-                                                                                                  partial_grouping_target,
-                                                                                                  NULL,
-                                                                                                  &total_groups);
-
                                add_path(grouped_rel, (Path *)
                                                 create_agg_path(root,
                                                                                 grouped_rel,
@@ -6274,25 +6204,24 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
                                                                                 havingQual,
                                                                                 agg_final_costs,
                                                                                 dNumGroups));
-                       }
                }
        }
 }
 
 /*
- * add_partial_paths_to_grouping_rel
+ * add_paths_to_partial_grouping_rel
  *
- * Add partial paths to grouping relation.  These paths are not fully
- * aggregated; a FinalizeAggregate step is still required.
+ * First, generate partially aggregated partial paths from the partial paths
+ * for the input relation, and then generate partially aggregated non-partial
+ * paths using Gather or Gather Merge.  All paths for this relation -- both
+ * partial and non-partial -- have been partially aggregated but require a
+ * subsequent FinalizeAggregate step.
  */
 static void
-add_partial_paths_to_grouping_rel(PlannerInfo *root,
+add_paths_to_partial_grouping_rel(PlannerInfo *root,
                                                                  RelOptInfo *input_rel,
-                                                                 RelOptInfo *grouped_rel,
-                                                                 PathTarget *target,
-                                                                 PathTarget *partial_grouping_target,
+                                                                 RelOptInfo *partially_grouped_rel,
                                                                  AggClauseCosts *agg_partial_costs,
-                                                                 AggClauseCosts *agg_final_costs,
                                                                  grouping_sets_data *gd,
                                                                  bool can_sort,
                                                                  bool can_hash,
@@ -6330,17 +6259,17 @@ add_partial_paths_to_grouping_rel(PlannerInfo *root,
                                /* Sort the cheapest partial path, if it isn't already */
                                if (!is_sorted)
                                        path = (Path *) create_sort_path(root,
-                                                                                                        grouped_rel,
+                                                                                                        partially_grouped_rel,
                                                                                                         path,
                                                                                                         root->group_pathkeys,
                                                                                                         -1.0);
 
                                if (parse->hasAggs)
-                                       add_partial_path(grouped_rel, (Path *)
+                                       add_partial_path(partially_grouped_rel, (Path *)
                                                                         create_agg_path(root,
-                                                                                                        grouped_rel,
+                                                                                                        partially_grouped_rel,
                                                                                                         path,
-                                                                                                        partial_grouping_target,
+                                                                                                        partially_grouped_rel->reltarget,
                                                                                                         parse->groupClause ? AGG_SORTED : AGG_PLAIN,
                                                                                                         AGGSPLIT_INITIAL_SERIAL,
                                                                                                         parse->groupClause,
@@ -6348,11 +6277,11 @@ add_partial_paths_to_grouping_rel(PlannerInfo *root,
                                                                                                         agg_partial_costs,
                                                                                                         dNumPartialGroups));
                                else
-                                       add_partial_path(grouped_rel, (Path *)
+                                       add_partial_path(partially_grouped_rel, (Path *)
                                                                         create_group_path(root,
-                                                                                                          grouped_rel,
+                                                                                                          partially_grouped_rel,
                                                                                                           path,
-                                                                                                          partial_grouping_target,
+                                                                                                          partially_grouped_rel->reltarget,
                                                                                                           parse->groupClause,
                                                                                                           NIL,
                                                                                                           dNumPartialGroups));
@@ -6376,11 +6305,11 @@ add_partial_paths_to_grouping_rel(PlannerInfo *root,
                 */
                if (hashaggtablesize < work_mem * 1024L)
                {
-                       add_partial_path(grouped_rel, (Path *)
+                       add_partial_path(partially_grouped_rel, (Path *)
                                                         create_agg_path(root,
-                                                                                        grouped_rel,
+                                                                                        partially_grouped_rel,
                                                                                         cheapest_partial_path,
-                                                                                        partial_grouping_target,
+                                                                                        partially_grouped_rel->reltarget,
                                                                                         AGG_HASHED,
                                                                                         AGGSPLIT_INITIAL_SERIAL,
                                                                                         parse->groupClause,
@@ -6389,6 +6318,58 @@ add_partial_paths_to_grouping_rel(PlannerInfo *root,
                                                                                         dNumPartialGroups));
                }
        }
+
+       /*
+        * If there is an FDW that's responsible for all baserels of the query,
+        * let it consider adding partially grouped ForeignPaths.
+        */
+       if (partially_grouped_rel->fdwroutine &&
+               partially_grouped_rel->fdwroutine->GetForeignUpperPaths)
+       {
+               FdwRoutine *fdwroutine = partially_grouped_rel->fdwroutine;
+
+               fdwroutine->GetForeignUpperPaths(root,
+                                                                                UPPERREL_PARTIAL_GROUP_AGG,
+                                                                                input_rel, partially_grouped_rel);
+       }
+
+       /*
+        * Try adding Gather or Gather Merge to partial paths to produce
+        * non-partial paths.
+        */
+       generate_gather_paths(root, partially_grouped_rel, true);
+
+       /*
+        * generate_gather_paths won't consider sorting the cheapest path to match
+        * the group keys and then applying a Gather Merge node to the result;
+        * that might be a winning strategy.
+        */
+       if (!pathkeys_contained_in(root->group_pathkeys,
+                                                          cheapest_partial_path->pathkeys))
+       {
+               Path       *path;
+               double          total_groups;
+
+               total_groups =
+                       cheapest_partial_path->rows * cheapest_partial_path->parallel_workers;
+               path = (Path *) create_sort_path(root, partially_grouped_rel,
+                                                                                cheapest_partial_path,
+                                                                                root->group_pathkeys,
+                                                                                -1.0);
+               path = (Path *)
+                       create_gather_merge_path(root,
+                                                                        partially_grouped_rel,
+                                                                        path,
+                                                                        partially_grouped_rel->reltarget,
+                                                                        root->group_pathkeys,
+                                                                        NULL,
+                                                                        &total_groups);
+
+               add_path(partially_grouped_rel, path);
+       }
+
+       /* Now choose the best path(s) */
+       set_cheapest(partially_grouped_rel);
 }
 
 /*
index b1c63173c22ffe2d701dfdc2f1498dce094bf98f..db8de2dfd0c4ffc87987713b0f9df1abe285f0cd 100644 (file)
@@ -71,6 +71,8 @@ typedef struct AggClauseCosts
 typedef enum UpperRelationKind
 {
        UPPERREL_SETOP,                         /* result of UNION/INTERSECT/EXCEPT, if any */
+       UPPERREL_PARTIAL_GROUP_AGG, /* result of partial grouping/aggregation, if
+                                                                * any */
        UPPERREL_GROUP_AGG,                     /* result of grouping/aggregation, if any */
        UPPERREL_WINDOW,                        /* result of window functions, if any */
        UPPERREL_DISTINCT,                      /* result of "SELECT DISTINCT", if any */
index b2b4353eeacff4dd60942033afe0bd52e68a9745..94f9bb2b574a6b64207baa88cf62ad4c04e72767 100644 (file)
@@ -53,7 +53,8 @@ extern void set_dummy_rel_pathlist(RelOptInfo *rel);
 extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,
                                         List *initial_rels);
 
-extern void generate_gather_paths(PlannerInfo *root, RelOptInfo *rel);
+extern void generate_gather_paths(PlannerInfo *root, RelOptInfo *rel,
+                                         bool override_rows);
 extern int compute_parallel_worker(RelOptInfo *rel, double heap_pages,
                                                double index_pages, int max_workers);
 extern void create_partial_bitmap_paths(PlannerInfo *root, RelOptInfo *rel,