mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
Split create_grouping_paths into degenerate and non-degenerate cases.
There's no functional change here, or at least I hope there isn't, just code rearrangement. The rearrangement is motivated by partition-wise aggregate, which doesn't need to consider the degenerate case but wants to reuse the logic for the ordinary case. Based loosely on a patch from Ashutosh Bapat and Jeevan Chalke, but I whacked it around pretty heavily. 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/CAFjFpRewpqCmVkwvq6qrRjmbMDpN0CZvRRzjd8UvncczA3Oz1Q@mail.gmail.com
This commit is contained in:
parent
a446a1c70f
commit
1466bcfa4a
@ -141,6 +141,16 @@ static RelOptInfo *create_grouping_paths(PlannerInfo *root,
|
|||||||
bool target_parallel_safe,
|
bool target_parallel_safe,
|
||||||
const AggClauseCosts *agg_costs,
|
const AggClauseCosts *agg_costs,
|
||||||
grouping_sets_data *gd);
|
grouping_sets_data *gd);
|
||||||
|
static bool is_degenerate_grouping(PlannerInfo *root);
|
||||||
|
static void create_degenerate_grouping_paths(PlannerInfo *root,
|
||||||
|
RelOptInfo *input_rel,
|
||||||
|
PathTarget *target, RelOptInfo *grouped_rel);
|
||||||
|
static void create_ordinary_grouping_paths(PlannerInfo *root,
|
||||||
|
RelOptInfo *input_rel,
|
||||||
|
PathTarget *target, RelOptInfo *grouped_rel,
|
||||||
|
RelOptInfo *partially_grouped_rel,
|
||||||
|
const AggClauseCosts *agg_costs,
|
||||||
|
grouping_sets_data *gd);
|
||||||
static void consider_groupingsets_paths(PlannerInfo *root,
|
static void consider_groupingsets_paths(PlannerInfo *root,
|
||||||
RelOptInfo *grouped_rel,
|
RelOptInfo *grouped_rel,
|
||||||
Path *path,
|
Path *path,
|
||||||
@ -3667,11 +3677,6 @@ estimate_hashagg_tablesize(Path *path, const AggClauseCosts *agg_costs,
|
|||||||
*
|
*
|
||||||
* Note: all Paths in input_rel are expected to return the target computed
|
* Note: all Paths in input_rel are expected to return the target computed
|
||||||
* by make_group_input_target.
|
* by make_group_input_target.
|
||||||
*
|
|
||||||
* We need to consider sorted and hashed aggregation in the same function,
|
|
||||||
* because otherwise (1) it would be harder to throw an appropriate error
|
|
||||||
* message if neither way works, and (2) we should not allow hashtable size
|
|
||||||
* considerations to dissuade us from using hashing if sorting is not possible.
|
|
||||||
*/
|
*/
|
||||||
static RelOptInfo *
|
static RelOptInfo *
|
||||||
create_grouping_paths(PlannerInfo *root,
|
create_grouping_paths(PlannerInfo *root,
|
||||||
@ -3682,15 +3687,8 @@ create_grouping_paths(PlannerInfo *root,
|
|||||||
grouping_sets_data *gd)
|
grouping_sets_data *gd)
|
||||||
{
|
{
|
||||||
Query *parse = root->parse;
|
Query *parse = root->parse;
|
||||||
Path *cheapest_path = input_rel->cheapest_total_path;
|
|
||||||
RelOptInfo *grouped_rel;
|
RelOptInfo *grouped_rel;
|
||||||
RelOptInfo *partially_grouped_rel;
|
RelOptInfo *partially_grouped_rel;
|
||||||
AggClauseCosts agg_partial_costs; /* parallel only */
|
|
||||||
AggClauseCosts agg_final_costs; /* parallel only */
|
|
||||||
double dNumGroups;
|
|
||||||
bool can_hash;
|
|
||||||
bool can_sort;
|
|
||||||
bool try_parallel_aggregation;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For now, all aggregated paths are added to the (GROUP_AGG, NULL)
|
* For now, all aggregated paths are added to the (GROUP_AGG, NULL)
|
||||||
@ -3728,73 +3726,123 @@ create_grouping_paths(PlannerInfo *root,
|
|||||||
partially_grouped_rel->fdwroutine = input_rel->fdwroutine;
|
partially_grouped_rel->fdwroutine = input_rel->fdwroutine;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for degenerate grouping.
|
* Create either paths for a degenerate grouping or paths for ordinary
|
||||||
|
* grouping, as appropriate.
|
||||||
*/
|
*/
|
||||||
if ((root->hasHavingQual || parse->groupingSets) &&
|
if (is_degenerate_grouping(root))
|
||||||
!parse->hasAggs && parse->groupClause == NIL)
|
create_degenerate_grouping_paths(root, input_rel, target, grouped_rel);
|
||||||
|
else
|
||||||
|
create_ordinary_grouping_paths(root, input_rel, target, grouped_rel,
|
||||||
|
partially_grouped_rel, agg_costs, gd);
|
||||||
|
|
||||||
|
set_cheapest(grouped_rel);
|
||||||
|
return grouped_rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* is_degenerate_grouping
|
||||||
|
*
|
||||||
|
* A degenerate grouping is one in which the query has a HAVING qual and/or
|
||||||
|
* grouping sets, but no aggregates and no GROUP BY (which implies that the
|
||||||
|
* grouping sets are all empty).
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
is_degenerate_grouping(PlannerInfo *root)
|
||||||
|
{
|
||||||
|
Query *parse = root->parse;
|
||||||
|
|
||||||
|
return (root->hasHavingQual || parse->groupingSets) &&
|
||||||
|
!parse->hasAggs && parse->groupClause == NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create_degenerate_grouping_paths
|
||||||
|
*
|
||||||
|
* When the grouping is degenerate (see is_degenerate_grouping), we are
|
||||||
|
* supposed to emit either zero or one row for each grouping set depending on
|
||||||
|
* whether HAVING succeeds. Furthermore, there cannot be any variables in
|
||||||
|
* either HAVING or the targetlist, so we actually do not need the FROM table
|
||||||
|
* at all! We can just throw away the plan-so-far and generate a Result node.
|
||||||
|
* This is a sufficiently unusual corner case that it's not worth contorting
|
||||||
|
* the structure of this module to avoid having to generate the earlier paths
|
||||||
|
* in the first place.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
create_degenerate_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
|
||||||
|
PathTarget *target, RelOptInfo *grouped_rel)
|
||||||
|
{
|
||||||
|
Query *parse = root->parse;
|
||||||
|
int nrows;
|
||||||
|
Path *path;
|
||||||
|
|
||||||
|
nrows = list_length(parse->groupingSets);
|
||||||
|
if (nrows > 1)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We have a HAVING qual and/or grouping sets, but no aggregates and
|
* Doesn't seem worthwhile writing code to cons up a generate_series
|
||||||
* no GROUP BY (which implies that the grouping sets are all empty).
|
* or a values scan to emit multiple rows. Instead just make N clones
|
||||||
*
|
* and append them. (With a volatile HAVING clause, this means you
|
||||||
* This is a degenerate case in which we are supposed to emit either
|
* might get between 0 and N output rows. Offhand I think that's
|
||||||
* zero or one row for each grouping set depending on whether HAVING
|
* desired.)
|
||||||
* succeeds. Furthermore, there cannot be any variables in either
|
|
||||||
* HAVING or the targetlist, so we actually do not need the FROM table
|
|
||||||
* at all! We can just throw away the plan-so-far and generate a
|
|
||||||
* Result node. This is a sufficiently unusual corner case that it's
|
|
||||||
* not worth contorting the structure of this module to avoid having
|
|
||||||
* to generate the earlier paths in the first place.
|
|
||||||
*/
|
*/
|
||||||
int nrows = list_length(parse->groupingSets);
|
List *paths = NIL;
|
||||||
Path *path;
|
|
||||||
|
|
||||||
if (nrows > 1)
|
while (--nrows >= 0)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* Doesn't seem worthwhile writing code to cons up a
|
|
||||||
* generate_series or a values scan to emit multiple rows. Instead
|
|
||||||
* just make N clones and append them. (With a volatile HAVING
|
|
||||||
* clause, this means you might get between 0 and N output rows.
|
|
||||||
* Offhand I think that's desired.)
|
|
||||||
*/
|
|
||||||
List *paths = NIL;
|
|
||||||
|
|
||||||
while (--nrows >= 0)
|
|
||||||
{
|
|
||||||
path = (Path *)
|
|
||||||
create_result_path(root, grouped_rel,
|
|
||||||
target,
|
|
||||||
(List *) parse->havingQual);
|
|
||||||
paths = lappend(paths, path);
|
|
||||||
}
|
|
||||||
path = (Path *)
|
|
||||||
create_append_path(grouped_rel,
|
|
||||||
paths,
|
|
||||||
NIL,
|
|
||||||
NULL,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
NIL,
|
|
||||||
-1);
|
|
||||||
path->pathtarget = target;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* No grouping sets, or just one, so one output row */
|
|
||||||
path = (Path *)
|
path = (Path *)
|
||||||
create_result_path(root, grouped_rel,
|
create_result_path(root, grouped_rel,
|
||||||
target,
|
target,
|
||||||
(List *) parse->havingQual);
|
(List *) parse->havingQual);
|
||||||
|
paths = lappend(paths, path);
|
||||||
}
|
}
|
||||||
|
path = (Path *)
|
||||||
add_path(grouped_rel, path);
|
create_append_path(grouped_rel,
|
||||||
|
paths,
|
||||||
/* No need to consider any other alternatives. */
|
NIL,
|
||||||
set_cheapest(grouped_rel);
|
NULL,
|
||||||
|
0,
|
||||||
return grouped_rel;
|
false,
|
||||||
|
NIL,
|
||||||
|
-1);
|
||||||
|
path->pathtarget = target;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No grouping sets, or just one, so one output row */
|
||||||
|
path = (Path *)
|
||||||
|
create_result_path(root, grouped_rel,
|
||||||
|
target,
|
||||||
|
(List *) parse->havingQual);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_path(grouped_rel, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create_ordinary_grouping_paths
|
||||||
|
*
|
||||||
|
* Create grouping paths for the ordinary (that is, non-degenerate) case.
|
||||||
|
*
|
||||||
|
* We need to consider sorted and hashed aggregation in the same function,
|
||||||
|
* because otherwise (1) it would be harder to throw an appropriate error
|
||||||
|
* message if neither way works, and (2) we should not allow hashtable size
|
||||||
|
* considerations to dissuade us from using hashing if sorting is not possible.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
|
||||||
|
PathTarget *target, RelOptInfo *grouped_rel,
|
||||||
|
RelOptInfo *partially_grouped_rel,
|
||||||
|
const AggClauseCosts *agg_costs,
|
||||||
|
grouping_sets_data *gd)
|
||||||
|
{
|
||||||
|
Query *parse = root->parse;
|
||||||
|
Path *cheapest_path = input_rel->cheapest_total_path;
|
||||||
|
AggClauseCosts agg_partial_costs; /* parallel only */
|
||||||
|
AggClauseCosts agg_final_costs; /* parallel only */
|
||||||
|
double dNumGroups;
|
||||||
|
bool can_hash;
|
||||||
|
bool can_sort;
|
||||||
|
bool try_parallel_aggregation;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Estimate number of groups.
|
* Estimate number of groups.
|
||||||
@ -3922,14 +3970,8 @@ create_grouping_paths(PlannerInfo *root,
|
|||||||
if (create_upper_paths_hook)
|
if (create_upper_paths_hook)
|
||||||
(*create_upper_paths_hook) (root, UPPERREL_GROUP_AGG,
|
(*create_upper_paths_hook) (root, UPPERREL_GROUP_AGG,
|
||||||
input_rel, grouped_rel);
|
input_rel, grouped_rel);
|
||||||
|
|
||||||
/* Now choose the best path(s) */
|
|
||||||
set_cheapest(grouped_rel);
|
|
||||||
|
|
||||||
return grouped_rel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For a given input path, consider the possible ways of doing grouping sets on
|
* For a given input path, consider the possible ways of doing grouping sets on
|
||||||
* it, by combinations of hashing and sorting. This can be called multiple
|
* it, by combinations of hashing and sorting. This can be called multiple
|
||||||
|
Loading…
x
Reference in New Issue
Block a user