mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Restore preprocess_groupclause()
0452b461bc
made optimizer explore alternative orderings of group-by pathkeys. It eliminated preprocess_groupclause(), which was intended to match items between GROUP BY and ORDER BY. Instead, get_useful_group_keys_orderings() function generates orderings of GROUP BY elements at the time of grouping paths generation. The get_useful_group_keys_orderings() function takes into account 3 orderings of GROUP BY pathkeys and clauses: original order as written in GROUP BY, matching ORDER BY clauses as much as possible, and matching the input path as much as possible. Given that even before0452b461b
, preprocess_groupclause() could change the original order of GROUP BY clauses we don't need to consider it apart from ordering matching ORDER BY clauses. This commit restores preprocess_groupclause() to provide an ordering of GROUP BY elements matching ORDER BY before generation of paths. The new version of preprocess_groupclause() takes into account an incremental sort. The get_useful_group_keys_orderings() function now takes into 2 orderings of GROUP BY elements: the order generated preprocess_groupclause() and the order matching the input path as much as possible. Discussion: https://postgr.es/m/CAPpHfdvyWLMGwvxaf%3D7KAp-z-4mxbSH8ti2f6mNOQv5metZFzg%40mail.gmail.com Author: Alexander Korotkov Reviewed-by: Andrei Lepikhov, Pavel Borisov
This commit is contained in:
@ -447,26 +447,6 @@ group_keys_reorder_by_pathkeys(List *pathkeys, List **group_pathkeys,
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* pathkeys_are_duplicate
|
||||
* Check if give pathkeys are already contained the list of
|
||||
* GroupByOrdering's.
|
||||
*/
|
||||
static bool
|
||||
pathkeys_are_duplicate(List *infos, List *pathkeys)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, infos)
|
||||
{
|
||||
GroupByOrdering *info = lfirst_node(GroupByOrdering, 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.
|
||||
@ -475,11 +455,11 @@ pathkeys_are_duplicate(List *infos, List *pathkeys)
|
||||
* 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 function considers (and keeps) following 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).
|
||||
* - GROUP BY keys as ordered by preprocess_groupclause() to match target
|
||||
* ORDER BY clause (as much as possible),
|
||||
* - GROUP BY keys reordered to match 'path' ordering (as much as possible).
|
||||
*/
|
||||
List *
|
||||
get_useful_group_keys_orderings(PlannerInfo *root, Path *path)
|
||||
@ -526,32 +506,7 @@ get_useful_group_keys_orderings(PlannerInfo *root, Path *path)
|
||||
|
||||
if (n > 0 &&
|
||||
(enable_incremental_sort || n == root->num_groupby_pathkeys) &&
|
||||
!pathkeys_are_duplicate(infos, pathkeys))
|
||||
{
|
||||
info = makeNode(GroupByOrdering);
|
||||
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))
|
||||
compare_pathkeys(pathkeys, root->group_pathkeys) != PATHKEYS_EQUAL)
|
||||
{
|
||||
info = makeNode(GroupByOrdering);
|
||||
info->pathkeys = pathkeys;
|
||||
|
@ -137,7 +137,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 *groupclause_apply_groupingset(PlannerInfo *root, List *force);
|
||||
static List *preprocess_groupclause(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);
|
||||
@ -1422,7 +1422,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction,
|
||||
else if (parse->groupClause)
|
||||
{
|
||||
/* Preprocess regular GROUP BY clause, if any */
|
||||
root->processed_groupClause = list_copy(parse->groupClause);
|
||||
root->processed_groupClause = preprocess_groupclause(root, NIL);
|
||||
/* Remove any redundant GROUP BY columns */
|
||||
remove_useless_groupby_columns(root);
|
||||
}
|
||||
@ -2169,7 +2169,7 @@ preprocess_grouping_sets(PlannerInfo *root)
|
||||
* The groupClauses for hashed grouping sets are built later on.)
|
||||
*/
|
||||
if (gs->set)
|
||||
rollup->groupClause = groupclause_apply_groupingset(root, gs->set);
|
||||
rollup->groupClause = preprocess_groupclause(root, gs->set);
|
||||
else
|
||||
rollup->groupClause = NIL;
|
||||
|
||||
@ -2821,24 +2821,106 @@ remove_useless_groupby_columns(PlannerInfo *root)
|
||||
}
|
||||
|
||||
/*
|
||||
* groupclause_apply_groupingset
|
||||
* Apply the order of GROUP BY clauses defined by grouping sets. Items
|
||||
* not in the grouping set are skipped.
|
||||
* 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.
|
||||
* We also consider partial match between GROUP BY and ORDER BY elements,
|
||||
* which could allow to implement ORDER BY using the incremental sort.
|
||||
*
|
||||
* We also 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. This is implemented during the generation of grouping
|
||||
* paths. See get_useful_group_keys_orderings() for details.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static List *
|
||||
groupclause_apply_groupingset(PlannerInfo *root, List *gset)
|
||||
preprocess_groupclause(PlannerInfo *root, List *force)
|
||||
{
|
||||
Query *parse = root->parse;
|
||||
List *new_groupclause = NIL;
|
||||
ListCell *sl;
|
||||
ListCell *gl;
|
||||
|
||||
foreach(sl, gset)
|
||||
/* For grouping sets, we need to force the ordering */
|
||||
if (force)
|
||||
{
|
||||
Index ref = lfirst_int(sl);
|
||||
SortGroupClause *cl = get_sortgroupref_clause(ref, parse->groupClause);
|
||||
foreach(sl, force)
|
||||
{
|
||||
Index ref = lfirst_int(sl);
|
||||
SortGroupClause *cl = get_sortgroupref_clause(ref, parse->groupClause);
|
||||
|
||||
new_groupclause = lappend(new_groupclause, cl);
|
||||
new_groupclause = lappend(new_groupclause, cl);
|
||||
}
|
||||
|
||||
return new_groupclause;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
|
||||
|
||||
/* 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. We don't require a
|
||||
* complete match, because even partial match allows ORDER BY to be
|
||||
* implemented using incremental 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 (!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;
|
||||
}
|
||||
|
||||
@ -4170,7 +4252,7 @@ consider_groupingsets_paths(PlannerInfo *root,
|
||||
{
|
||||
rollup = makeNode(RollupData);
|
||||
|
||||
rollup->groupClause = groupclause_apply_groupingset(root, gset);
|
||||
rollup->groupClause = preprocess_groupclause(root, gset);
|
||||
rollup->gsets_data = list_make1(gs);
|
||||
rollup->gsets = remap_to_groupclause_idx(rollup->groupClause,
|
||||
rollup->gsets_data,
|
||||
@ -4359,7 +4441,7 @@ consider_groupingsets_paths(PlannerInfo *root,
|
||||
|
||||
Assert(gs->set != NIL);
|
||||
|
||||
rollup->groupClause = groupclause_apply_groupingset(root, gs->set);
|
||||
rollup->groupClause = preprocess_groupclause(root, gs->set);
|
||||
rollup->gsets_data = list_make1(gs);
|
||||
rollup->gsets = remap_to_groupclause_idx(rollup->groupClause,
|
||||
rollup->gsets_data,
|
||||
|
Reference in New Issue
Block a user