diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/equivclass.c | 13 | ||||
-rw-r--r-- | src/backend/optimizer/path/pathkeys.c | 252 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 424 |
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; |