Commit 1466bcfa authored by Robert Haas's avatar Robert Haas

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
parent a446a1c7
...@@ -141,6 +141,16 @@ static RelOptInfo *create_grouping_paths(PlannerInfo *root, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 *)
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 *)
create_result_path(root, grouped_rel,
target,
(List *) parse->havingQual);
}
add_path(grouped_rel, path); add_path(grouped_rel, path);
}
/* No need to consider any other alternatives. */
set_cheapest(grouped_rel);
return grouped_rel; /*
} * 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, ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment