aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/equivclass.c13
-rw-r--r--src/backend/optimizer/path/pathkeys.c252
-rw-r--r--src/backend/optimizer/plan/planner.c424
3 files changed, 467 insertions, 222 deletions
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e86dfeaecd4..4bd60a09c69 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -652,7 +652,18 @@ get_eclass_for_sort_expr(PlannerInfo *root,
if (opcintype == cur_em->em_datatype &&
equal(expr, cur_em->em_expr))
- return cur_ec; /* Match! */
+ {
+ /*
+ * Match!
+ *
+ * Copy the sortref if it wasn't set yet. That may happen if
+ * the ec was constructed from a WHERE clause, i.e. it doesn't
+ * have a target reference at all.
+ */
+ if (cur_ec->ec_sortref == 0 && sortref > 0)
+ cur_ec->ec_sortref = sortref;
+ return cur_ec;
+ }
}
}
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index ca94a31f71e..82ff31273b9 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -22,12 +22,15 @@
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
+#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "partitioning/partbounds.h"
#include "utils/lsyscache.h"
+/* Consider reordering of GROUP BY keys? */
+bool enable_group_by_reordering = true;
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
static bool matches_boolean_partition_clause(RestrictInfo *rinfo,
@@ -351,6 +354,202 @@ pathkeys_contained_in(List *keys1, List *keys2)
}
/*
+ * group_keys_reorder_by_pathkeys
+ * Reorder GROUP BY keys to match the input pathkeys.
+ *
+ * Function returns new lists (pathkeys and clauses), original GROUP BY lists
+ * stay untouched.
+ *
+ * Returns the number of GROUP BY keys with a matching pathkey.
+ */
+static int
+group_keys_reorder_by_pathkeys(List *pathkeys, List **group_pathkeys,
+ List **group_clauses,
+ int num_groupby_pathkeys)
+{
+ List *new_group_pathkeys = NIL,
+ *new_group_clauses = NIL;
+ ListCell *lc;
+ int n;
+
+ if (pathkeys == NIL || *group_pathkeys == NIL)
+ return 0;
+
+ /*
+ * Walk the pathkeys (determining ordering of the input path) and see if
+ * there's a matching GROUP BY key. If we find one, we append it to the
+ * list, and do the same for the clauses.
+ *
+ * Once we find the first pathkey without a matching GROUP BY key, the
+ * rest of the pathkeys are useless and can't be used to evaluate the
+ * grouping, so we abort the loop and ignore the remaining pathkeys.
+ */
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = (PathKey *) lfirst(lc);
+ SortGroupClause *sgc;
+
+ /*
+ * Pathkeys are built in a way that allows simply comparing pointers.
+ * Give up if we can't find the matching pointer. Also give up if
+ * there is no sortclause reference for some reason.
+ */
+ if (foreach_current_index(lc) >= num_groupby_pathkeys ||
+ !list_member_ptr(*group_pathkeys, pathkey) ||
+ pathkey->pk_eclass->ec_sortref == 0)
+ break;
+
+ /*
+ * Since 1349d27 pathkey coming from underlying node can be in the
+ * root->group_pathkeys but not in the processed_groupClause. So, we
+ * should be careful here.
+ */
+ sgc = get_sortgroupref_clause_noerr(pathkey->pk_eclass->ec_sortref,
+ *group_clauses);
+ if (!sgc)
+ /* The grouping clause does not cover this pathkey */
+ break;
+
+ /*
+ * Sort group clause should have an ordering operator as long as there
+ * is an associated pathkey.
+ */
+ Assert(OidIsValid(sgc->sortop));
+
+ new_group_pathkeys = lappend(new_group_pathkeys, pathkey);
+ new_group_clauses = lappend(new_group_clauses, sgc);
+ }
+
+ /* remember the number of pathkeys with a matching GROUP BY key */
+ n = list_length(new_group_pathkeys);
+
+ /* append the remaining group pathkeys (will be treated as not sorted) */
+ *group_pathkeys = list_concat_unique_ptr(new_group_pathkeys,
+ *group_pathkeys);
+ *group_clauses = list_concat_unique_ptr(new_group_clauses,
+ *group_clauses);
+
+ return n;
+}
+
+/*
+ * pathkeys_are_duplicate
+ * Check if give pathkeys are already contained the list of
+ * PathKeyInfo's.
+ */
+static bool
+pathkeys_are_duplicate(List *infos, List *pathkeys)
+{
+ ListCell *lc;
+
+ foreach(lc, infos)
+ {
+ PathKeyInfo *info = lfirst_node(PathKeyInfo, lc);
+
+ if (compare_pathkeys(pathkeys, info->pathkeys) == PATHKEYS_EQUAL)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * get_useful_group_keys_orderings
+ * Determine which orderings of GROUP BY keys are potentially interesting.
+ *
+ * Returns a list of PathKeyInfo items, each representing an interesting
+ * ordering of GROUP BY keys. Each item stores pathkeys and clauses in the
+ * matching order.
+ *
+ * The function considers (and keeps) multiple GROUP BY orderings:
+ *
+ * - the original ordering, as specified by the GROUP BY clause,
+ * - GROUP BY keys reordered to match 'path' ordering (as much as possible),
+ * - GROUP BY keys to match target ORDER BY clause (as much as possible).
+ */
+List *
+get_useful_group_keys_orderings(PlannerInfo *root, Path *path)
+{
+ Query *parse = root->parse;
+ List *infos = NIL;
+ PathKeyInfo *info;
+
+ List *pathkeys = root->group_pathkeys;
+ List *clauses = root->processed_groupClause;
+
+ /* always return at least the original pathkeys/clauses */
+ info = makeNode(PathKeyInfo);
+ info->pathkeys = pathkeys;
+ info->clauses = clauses;
+ infos = lappend(infos, info);
+
+ /*
+ * Should we try generating alternative orderings of the group keys? If
+ * not, we produce only the order specified in the query, i.e. the
+ * optimization is effectively disabled.
+ */
+ if (!enable_group_by_reordering)
+ return infos;
+
+ /*
+ * Grouping sets have own and more complex logic to decide the ordering.
+ */
+ if (parse->groupingSets)
+ return infos;
+
+ /*
+ * If the path is sorted in some way, try reordering the group keys to
+ * match the path as much of the ordering as possible. Then thanks to
+ * incremental sort we would get this sort as cheap as possible.
+ */
+ if (path->pathkeys &&
+ !pathkeys_contained_in(path->pathkeys, root->group_pathkeys))
+ {
+ int n;
+
+ n = group_keys_reorder_by_pathkeys(path->pathkeys, &pathkeys, &clauses,
+ root->num_groupby_pathkeys);
+
+ if (n > 0 &&
+ (enable_incremental_sort || n == root->num_groupby_pathkeys) &&
+ !pathkeys_are_duplicate(infos, pathkeys))
+ {
+ info = makeNode(PathKeyInfo);
+ info->pathkeys = pathkeys;
+ info->clauses = clauses;
+
+ infos = lappend(infos, info);
+ }
+ }
+
+ /*
+ * Try reordering pathkeys to minimize the sort cost (this time consider
+ * the ORDER BY clause).
+ */
+ if (root->sort_pathkeys &&
+ !pathkeys_contained_in(root->sort_pathkeys, root->group_pathkeys))
+ {
+ int n;
+
+ n = group_keys_reorder_by_pathkeys(root->sort_pathkeys, &pathkeys,
+ &clauses,
+ root->num_groupby_pathkeys);
+
+ if (n > 0 &&
+ (enable_incremental_sort || n == list_length(root->sort_pathkeys)) &&
+ !pathkeys_are_duplicate(infos, pathkeys))
+ {
+ info = makeNode(PathKeyInfo);
+ info->pathkeys = pathkeys;
+ info->clauses = clauses;
+
+ infos = lappend(infos, info);
+ }
+ }
+
+ return infos;
+}
+
+/*
* pathkeys_count_contained_in
* Same as pathkeys_contained_in, but also sets length of longest
* common prefix of keys1 and keys2.
@@ -1940,6 +2139,54 @@ pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys)
}
/*
+ * pathkeys_useful_for_grouping
+ * Count the number of pathkeys that are useful for grouping (instead of
+ * explicit sort)
+ *
+ * Group pathkeys could be reordered to benefit from the ordering. The
+ * ordering may not be "complete" and may require incremental sort, but that's
+ * fine. So we simply count prefix pathkeys with a matching group key, and
+ * stop once we find the first pathkey without a match.
+ *
+ * So e.g. with pathkeys (a,b,c) and group keys (a,b,e) this determines (a,b)
+ * pathkeys are useful for grouping, and we might do incremental sort to get
+ * path ordered by (a,b,e).
+ *
+ * This logic is necessary to retain paths with ordering not matching grouping
+ * keys directly, without the reordering.
+ *
+ * Returns the length of pathkey prefix with matching group keys.
+ */
+static int
+pathkeys_useful_for_grouping(PlannerInfo *root, List *pathkeys)
+{
+ ListCell *key;
+ int n = 0;
+
+ /* no special ordering requested for grouping */
+ if (root->group_pathkeys == NIL)
+ return 0;
+
+ /* unordered path */
+ if (pathkeys == NIL)
+ return 0;
+
+ /* walk the pathkeys and search for matching group key */
+ foreach(key, pathkeys)
+ {
+ PathKey *pathkey = (PathKey *) lfirst(key);
+
+ /* no matching group key, we're done */
+ if (!list_member_ptr(root->group_pathkeys, pathkey))
+ break;
+
+ n++;
+ }
+
+ return n;
+}
+
+/*
* truncate_useless_pathkeys
* Shorten the given pathkey list to just the useful pathkeys.
*/
@@ -1955,6 +2202,9 @@ truncate_useless_pathkeys(PlannerInfo *root,
nuseful2 = pathkeys_useful_for_ordering(root, pathkeys);
if (nuseful2 > nuseful)
nuseful = nuseful2;
+ nuseful2 = pathkeys_useful_for_grouping(root, pathkeys);
+ if (nuseful2 > nuseful)
+ nuseful = nuseful2;
/*
* Note: not safe to modify input list destructively, but we can avoid
@@ -1988,6 +2238,8 @@ has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel)
{
if (rel->joininfo != NIL || rel->has_eclass_joins)
return true; /* might be able to use pathkeys for merging */
+ if (root->group_pathkeys != NIL)
+ return true; /* might be able to use pathkeys for grouping */
if (root->query_pathkeys != NIL)
return true; /* might be able to use them for ordering */
return false; /* definitely useless */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 014b179c3f0..2e2458b1284 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -140,7 +140,7 @@ static double preprocess_limit(PlannerInfo *root,
double tuple_fraction,
int64 *offset_est, int64 *count_est);
static void remove_useless_groupby_columns(PlannerInfo *root);
-static List *preprocess_groupclause(PlannerInfo *root, List *force);
+static List *groupclause_apply_groupingset(PlannerInfo *root, List *force);
static List *extract_rollup_sets(List *groupingSets);
static List *reorder_grouping_sets(List *groupingSets, List *sortclause);
static void standard_qp_callback(PlannerInfo *root, void *extra);
@@ -1423,7 +1423,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
else if (parse->groupClause)
{
/* Preprocess regular GROUP BY clause, if any */
- root->processed_groupClause = preprocess_groupclause(root, NIL);
+ root->processed_groupClause = list_copy(parse->groupClause);;
/* Remove any redundant GROUP BY columns */
remove_useless_groupby_columns(root);
}
@@ -2144,7 +2144,7 @@ preprocess_grouping_sets(PlannerInfo *root)
* The groupClauses for hashed grouping sets are built later on.)
*/
if (gs->set)
- rollup->groupClause = preprocess_groupclause(root, gs->set);
+ rollup->groupClause = groupclause_apply_groupingset(root, gs->set);
else
rollup->groupClause = NIL;
@@ -2796,111 +2796,24 @@ remove_useless_groupby_columns(PlannerInfo *root)
}
/*
- * preprocess_groupclause - do preparatory work on GROUP BY clause
- *
- * The idea here is to adjust the ordering of the GROUP BY elements
- * (which in itself is semantically insignificant) to match ORDER BY,
- * thereby allowing a single sort operation to both implement the ORDER BY
- * requirement and set up for a Unique step that implements GROUP BY.
- *
- * In principle it might be interesting to consider other orderings of the
- * GROUP BY elements, which could match the sort ordering of other
- * possible plans (eg an indexscan) and thereby reduce cost. We don't
- * bother with that, though. Hashed grouping will frequently win anyway.
- *
- * Note: we need no comparable processing of the distinctClause because
- * the parser already enforced that that matches ORDER BY.
- *
- * Note: we return a fresh List, but its elements are the same
- * SortGroupClauses appearing in parse->groupClause. This is important
- * because later processing may modify the processed_groupClause list.
- *
- * For grouping sets, the order of items is instead forced to agree with that
- * of the grouping set (and items not in the grouping set are skipped). The
- * work of sorting the order of grouping set elements to match the ORDER BY if
- * possible is done elsewhere.
+ * groupclause_apply_groupingset
+ * Apply the order of GROUP BY clauses defined by grouping sets. Items
+ * not in the grouping set are skipped.
*/
static List *
-preprocess_groupclause(PlannerInfo *root, List *force)
+groupclause_apply_groupingset(PlannerInfo *root, List *gset)
{
Query *parse = root->parse;
List *new_groupclause = NIL;
- bool partial_match;
ListCell *sl;
- ListCell *gl;
- /* For grouping sets, we need to force the ordering */
- if (force)
+ foreach(sl, gset)
{
- foreach(sl, force)
- {
- Index ref = lfirst_int(sl);
- SortGroupClause *cl = get_sortgroupref_clause(ref, parse->groupClause);
+ Index ref = lfirst_int(sl);
+ SortGroupClause *cl = get_sortgroupref_clause(ref, parse->groupClause);
- new_groupclause = lappend(new_groupclause, cl);
- }
-
- return new_groupclause;
+ new_groupclause = lappend(new_groupclause, cl);
}
-
- /* If no ORDER BY, nothing useful to do here */
- if (parse->sortClause == NIL)
- return list_copy(parse->groupClause);
-
- /*
- * Scan the ORDER BY clause and construct a list of matching GROUP BY
- * items, but only as far as we can make a matching prefix.
- *
- * This code assumes that the sortClause contains no duplicate items.
- */
- foreach(sl, parse->sortClause)
- {
- SortGroupClause *sc = lfirst_node(SortGroupClause, sl);
-
- foreach(gl, parse->groupClause)
- {
- SortGroupClause *gc = lfirst_node(SortGroupClause, gl);
-
- if (equal(gc, sc))
- {
- new_groupclause = lappend(new_groupclause, gc);
- break;
- }
- }
- if (gl == NULL)
- break; /* no match, so stop scanning */
- }
-
- /* Did we match all of the ORDER BY list, or just some of it? */
- partial_match = (sl != NULL);
-
- /* If no match at all, no point in reordering GROUP BY */
- if (new_groupclause == NIL)
- return list_copy(parse->groupClause);
-
- /*
- * Add any remaining GROUP BY items to the new list, but only if we were
- * able to make a complete match. In other words, we only rearrange the
- * GROUP BY list if the result is that one list is a prefix of the other
- * --- otherwise there's no possibility of a common sort. Also, give up
- * if there are any non-sortable GROUP BY items, since then there's no
- * hope anyway.
- */
- foreach(gl, parse->groupClause)
- {
- SortGroupClause *gc = lfirst_node(SortGroupClause, gl);
-
- if (list_member_ptr(new_groupclause, gc))
- continue; /* it matched an ORDER BY item */
- if (partial_match) /* give up, no common sort possible */
- return list_copy(parse->groupClause);
- if (!OidIsValid(gc->sortop)) /* give up, GROUP BY can't be sorted */
- return list_copy(parse->groupClause);
- new_groupclause = lappend(new_groupclause, gc);
- }
-
- /* Success --- install the rearranged GROUP BY list */
- Assert(list_length(parse->groupClause) == list_length(new_groupclause));
return new_groupclause;
}
@@ -4200,7 +4113,7 @@ consider_groupingsets_paths(PlannerInfo *root,
{
rollup = makeNode(RollupData);
- rollup->groupClause = preprocess_groupclause(root, gset);
+ rollup->groupClause = groupclause_apply_groupingset(root, gset);
rollup->gsets_data = list_make1(gs);
rollup->gsets = remap_to_groupclause_idx(rollup->groupClause,
rollup->gsets_data,
@@ -4389,7 +4302,7 @@ consider_groupingsets_paths(PlannerInfo *root,
Assert(gs->set != NIL);
- rollup->groupClause = preprocess_groupclause(root, gs->set);
+ rollup->groupClause = groupclause_apply_groupingset(root, gs->set);
rollup->gsets_data = list_make1(gs);
rollup->gsets = remap_to_groupclause_idx(rollup->groupClause,
rollup->gsets_data,
@@ -6891,103 +6804,135 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
*/
foreach(lc, input_rel->pathlist)
{
+ ListCell *lc2;
Path *path = (Path *) lfirst(lc);
+ Path *path_save = path;
+ List *pathkey_orderings = NIL;
- path = make_ordered_path(root,
- grouped_rel,
- path,
- cheapest_path,
- root->group_pathkeys);
+ /* generate alternative group orderings that might be useful */
+ pathkey_orderings = get_useful_group_keys_orderings(root, path);
- if (path == NULL)
- continue;
+ Assert(list_length(pathkey_orderings) > 0);
- /* Now decide what to stick atop it */
- if (parse->groupingSets)
- {
- consider_groupingsets_paths(root, grouped_rel,
- path, true, can_hash,
- gd, agg_costs, dNumGroups);
- }
- else if (parse->hasAggs)
- {
- /*
- * We have aggregation, possibly with plain GROUP BY. Make an
- * AggPath.
- */
- add_path(grouped_rel, (Path *)
- create_agg_path(root,
- grouped_rel,
- path,
- grouped_rel->reltarget,
- parse->groupClause ? AGG_SORTED : AGG_PLAIN,
- AGGSPLIT_SIMPLE,
- root->processed_groupClause,
- havingQual,
- agg_costs,
- dNumGroups));
- }
- else if (parse->groupClause)
+ foreach(lc2, pathkey_orderings)
{
- /*
- * We have GROUP BY without aggregation or grouping sets. Make
- * a GroupPath.
- */
- add_path(grouped_rel, (Path *)
- create_group_path(root,
- grouped_rel,
- path,
- root->processed_groupClause,
- havingQual,
- dNumGroups));
- }
- else
- {
- /* Other cases should have been handled above */
- Assert(false);
- }
- }
+ PathKeyInfo *info = (PathKeyInfo *) lfirst(lc2);
- /*
- * Instead of operating directly on the input relation, we can
- * consider finalizing a partially aggregated path.
- */
- if (partially_grouped_rel != NULL)
- {
- foreach(lc, partially_grouped_rel->pathlist)
- {
- Path *path = (Path *) lfirst(lc);
+ /* restore the path (we replace it in the loop) */
+ path = path_save;
path = make_ordered_path(root,
grouped_rel,
path,
- partially_grouped_rel->cheapest_total_path,
- root->group_pathkeys);
-
+ cheapest_path,
+ info->pathkeys);
if (path == NULL)
continue;
- if (parse->hasAggs)
+ /* Now decide what to stick atop it */
+ if (parse->groupingSets)
+ {
+ consider_groupingsets_paths(root, grouped_rel,
+ path, true, can_hash,
+ gd, agg_costs, dNumGroups);
+ }
+ else if (parse->hasAggs)
+ {
+ /*
+ * We have aggregation, possibly with plain GROUP BY. Make
+ * an AggPath.
+ */
add_path(grouped_rel, (Path *)
create_agg_path(root,
grouped_rel,
path,
grouped_rel->reltarget,
parse->groupClause ? AGG_SORTED : AGG_PLAIN,
- AGGSPLIT_FINAL_DESERIAL,
- root->processed_groupClause,
+ AGGSPLIT_SIMPLE,
+ info->clauses,
havingQual,
- agg_final_costs,
+ agg_costs,
dNumGroups));
- else
+ }
+ else if (parse->groupClause)
+ {
+ /*
+ * We have GROUP BY without aggregation or grouping sets.
+ * Make a GroupPath.
+ */
add_path(grouped_rel, (Path *)
create_group_path(root,
grouped_rel,
path,
- root->processed_groupClause,
+ info->clauses,
havingQual,
dNumGroups));
+ }
+ else
+ {
+ /* Other cases should have been handled above */
+ Assert(false);
+ }
+ }
+ }
+ /*
+ * Instead of operating directly on the input relation, we can
+ * consider finalizing a partially aggregated path.
+ */
+ if (partially_grouped_rel != NULL)
+ {
+ foreach(lc, partially_grouped_rel->pathlist)
+ {
+ ListCell *lc2;
+ Path *path = (Path *) lfirst(lc);
+ Path *path_save = path;
+ List *pathkey_orderings = NIL;
+
+ /* generate alternative group orderings that might be useful */
+ pathkey_orderings = get_useful_group_keys_orderings(root, path);
+
+ Assert(list_length(pathkey_orderings) > 0);
+
+ /* process all potentially interesting grouping reorderings */
+ foreach(lc2, pathkey_orderings)
+ {
+ PathKeyInfo *info = (PathKeyInfo *) lfirst(lc2);
+
+ /* restore the path (we replace it in the loop) */
+ path = path_save;
+
+ path = make_ordered_path(root,
+ grouped_rel,
+ path,
+ partially_grouped_rel->cheapest_total_path,
+ info->pathkeys);
+
+ if (path == NULL)
+ continue;
+
+ if (parse->hasAggs)
+ add_path(grouped_rel, (Path *)
+ create_agg_path(root,
+ grouped_rel,
+ path,
+ grouped_rel->reltarget,
+ parse->groupClause ? AGG_SORTED : AGG_PLAIN,
+ AGGSPLIT_FINAL_DESERIAL,
+ info->clauses,
+ havingQual,
+ agg_final_costs,
+ dNumGroups));
+ else
+ add_path(grouped_rel, (Path *)
+ create_group_path(root,
+ grouped_rel,
+ path,
+ info->clauses,
+ havingQual,
+ dNumGroups));
+
+ }
}
}
}
@@ -7190,37 +7135,54 @@ create_partial_grouping_paths(PlannerInfo *root,
*/
foreach(lc, input_rel->pathlist)
{
+ ListCell *lc2;
Path *path = (Path *) lfirst(lc);
+ Path *path_save = path;
+ List *pathkey_orderings = NIL;
- path = make_ordered_path(root,
- partially_grouped_rel,
- path,
- cheapest_total_path,
- root->group_pathkeys);
+ /* generate alternative group orderings that might be useful */
+ pathkey_orderings = get_useful_group_keys_orderings(root, path);
- if (path == NULL)
- continue;
+ Assert(list_length(pathkey_orderings) > 0);
- if (parse->hasAggs)
- add_path(partially_grouped_rel, (Path *)
- create_agg_path(root,
+ /* process all potentially interesting grouping reorderings */
+ foreach(lc2, pathkey_orderings)
+ {
+ PathKeyInfo *info = (PathKeyInfo *) lfirst(lc2);
+
+ /* restore the path (we replace it in the loop) */
+ path = path_save;
+
+ path = make_ordered_path(root,
partially_grouped_rel,
path,
- partially_grouped_rel->reltarget,
- parse->groupClause ? AGG_SORTED : AGG_PLAIN,
- AGGSPLIT_INITIAL_SERIAL,
- root->processed_groupClause,
- NIL,
- agg_partial_costs,
- dNumPartialGroups));
- else
- add_path(partially_grouped_rel, (Path *)
- create_group_path(root,
- partially_grouped_rel,
- path,
- root->processed_groupClause,
- NIL,
- dNumPartialGroups));
+ cheapest_total_path,
+ info->pathkeys);
+
+ if (path == NULL)
+ continue;
+
+ if (parse->hasAggs)
+ add_path(partially_grouped_rel, (Path *)
+ create_agg_path(root,
+ partially_grouped_rel,
+ path,
+ partially_grouped_rel->reltarget,
+ parse->groupClause ? AGG_SORTED : AGG_PLAIN,
+ AGGSPLIT_INITIAL_SERIAL,
+ info->clauses,
+ NIL,
+ agg_partial_costs,
+ dNumPartialGroups));
+ else
+ add_path(partially_grouped_rel, (Path *)
+ create_group_path(root,
+ partially_grouped_rel,
+ path,
+ info->clauses,
+ NIL,
+ dNumPartialGroups));
+ }
}
}
@@ -7229,37 +7191,55 @@ create_partial_grouping_paths(PlannerInfo *root,
/* Similar to above logic, but for partial paths. */
foreach(lc, input_rel->partial_pathlist)
{
+ ListCell *lc2;
Path *path = (Path *) lfirst(lc);
+ Path *path_save = path;
+ List *pathkey_orderings = NIL;
- path = make_ordered_path(root,
- partially_grouped_rel,
- path,
- cheapest_partial_path,
- root->group_pathkeys);
+ /* generate alternative group orderings that might be useful */
+ pathkey_orderings = get_useful_group_keys_orderings(root, path);
- if (path == NULL)
- continue;
+ Assert(list_length(pathkey_orderings) > 0);
- if (parse->hasAggs)
- add_partial_path(partially_grouped_rel, (Path *)
- create_agg_path(root,
- partially_grouped_rel,
- path,
- partially_grouped_rel->reltarget,
- parse->groupClause ? AGG_SORTED : AGG_PLAIN,
- AGGSPLIT_INITIAL_SERIAL,
- root->processed_groupClause,
- NIL,
- agg_partial_costs,
- dNumPartialPartialGroups));
- else
- add_partial_path(partially_grouped_rel, (Path *)
- create_group_path(root,
- partially_grouped_rel,
- path,
- root->processed_groupClause,
- NIL,
- dNumPartialPartialGroups));
+ /* process all potentially interesting grouping reorderings */
+ foreach(lc2, pathkey_orderings)
+ {
+ PathKeyInfo *info = (PathKeyInfo *) lfirst(lc2);
+
+
+ /* restore the path (we replace it in the loop) */
+ path = path_save;
+
+ path = make_ordered_path(root,
+ partially_grouped_rel,
+ path,
+ cheapest_partial_path,
+ info->pathkeys);
+
+ if (path == NULL)
+ continue;
+
+ if (parse->hasAggs)
+ add_partial_path(partially_grouped_rel, (Path *)
+ create_agg_path(root,
+ partially_grouped_rel,
+ path,
+ partially_grouped_rel->reltarget,
+ parse->groupClause ? AGG_SORTED : AGG_PLAIN,
+ AGGSPLIT_INITIAL_SERIAL,
+ info->clauses,
+ NIL,
+ agg_partial_costs,
+ dNumPartialPartialGroups));
+ else
+ add_partial_path(partially_grouped_rel, (Path *)
+ create_group_path(root,
+ partially_grouped_rel,
+ path,
+ info->clauses,
+ NIL,
+ dNumPartialPartialGroups));
+ }
}
}
@@ -7373,6 +7353,8 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
* We can also skip the entire loop when we only have a single-item
* group_pathkeys because then we can't possibly have a presorted prefix
* of the list without having the list be fully sorted.
+ *
+ * XXX Shouldn't this also consider the group-key-reordering?
*/
if (!enable_incremental_sort || list_length(root->group_pathkeys) == 1)
return;