Commit b5996c27 authored by Robert Haas's avatar Robert Haas

Determine grouping strategies in create_grouping_paths.

Partition-wise aggregate will call create_ordinary_grouping_paths
multiple times and we don't want to redo this work every time; have
the caller do it instead and pass the details down.

Patch by me, reviewed by Ashutosh Bapat.

Discussion: http://postgr.es/m/CA+TgmoY7VYYn9a7YHj1nJL6zj6BkHmt4K-un9LRmXkyqRZyynA@mail.gmail.com
parent 4f15e5d0
...@@ -93,6 +93,25 @@ typedef struct ...@@ -93,6 +93,25 @@ typedef struct
List *groupClause; /* overrides parse->groupClause */ List *groupClause; /* overrides parse->groupClause */
} standard_qp_extra; } standard_qp_extra;
/*
* Various flags indicating what kinds of grouping are possible.
*
* GROUPING_CAN_USE_SORT should be set if it's possible to perform
* sort-based implementations of grouping. When grouping sets are in use,
* this will be true if sorting is potentially usable for any of the grouping
* sets, even if it's not usable for all of them.
*
* GROUPING_CAN_USE_HASH should be set if it's possible to perform
* hash-based implementations of grouping.
*
* GROUPING_CAN_PARTIAL_AGG should be set if the aggregation is of a type
* for which we support partial aggregation (not, for example, grouping sets).
* It says nothing about parallel-safety or the availability of suitable paths.
*/
#define GROUPING_CAN_USE_SORT 0x0001
#define GROUPING_CAN_USE_HASH 0x0002
#define GROUPING_CAN_PARTIAL_AGG 0x0004
/* /*
* Data specific to grouping sets * Data specific to grouping sets
*/ */
...@@ -149,7 +168,7 @@ static void create_ordinary_grouping_paths(PlannerInfo *root, ...@@ -149,7 +168,7 @@ static void create_ordinary_grouping_paths(PlannerInfo *root,
RelOptInfo *input_rel, RelOptInfo *input_rel,
PathTarget *target, RelOptInfo *grouped_rel, PathTarget *target, RelOptInfo *grouped_rel,
const AggClauseCosts *agg_costs, const AggClauseCosts *agg_costs,
grouping_sets_data *gd); grouping_sets_data *gd, int flags);
static void consider_groupingsets_paths(PlannerInfo *root, static void consider_groupingsets_paths(PlannerInfo *root,
RelOptInfo *grouped_rel, RelOptInfo *grouped_rel,
Path *path, Path *path,
...@@ -215,8 +234,8 @@ static RelOptInfo *create_partial_grouping_paths(PlannerInfo *root, ...@@ -215,8 +234,8 @@ static RelOptInfo *create_partial_grouping_paths(PlannerInfo *root,
bool can_hash, bool can_hash,
AggClauseCosts *agg_final_costs); AggClauseCosts *agg_final_costs);
static void gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel); static void gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel);
static bool can_parallel_agg(PlannerInfo *root, RelOptInfo *input_rel, static bool can_partial_agg(PlannerInfo *root,
RelOptInfo *grouped_rel, const AggClauseCosts *agg_costs); const AggClauseCosts *agg_costs);
/***************************************************************************** /*****************************************************************************
...@@ -3720,8 +3739,58 @@ create_grouping_paths(PlannerInfo *root, ...@@ -3720,8 +3739,58 @@ create_grouping_paths(PlannerInfo *root,
if (is_degenerate_grouping(root)) if (is_degenerate_grouping(root))
create_degenerate_grouping_paths(root, input_rel, target, grouped_rel); create_degenerate_grouping_paths(root, input_rel, target, grouped_rel);
else else
{
int flags = 0;
/*
* Determine whether it's possible to perform sort-based
* implementations of grouping. (Note that if groupClause is empty,
* grouping_is_sortable() is trivially true, and all the
* pathkeys_contained_in() tests will succeed too, so that we'll
* consider every surviving input path.)
*
* If we have grouping sets, we might be able to sort some but not all
* of them; in this case, we need can_sort to be true as long as we
* must consider any sorted-input plan.
*/
if ((gd && gd->rollups != NIL)
|| grouping_is_sortable(parse->groupClause))
flags |= GROUPING_CAN_USE_SORT;
/*
* Determine whether we should consider hash-based implementations of
* grouping.
*
* Hashed aggregation only applies if we're grouping. If we have
* grouping sets, some groups might be hashable but others not; in
* this case we set can_hash true as long as there is nothing globally
* preventing us from hashing (and we should therefore consider plans
* with hashes).
*
* Executor doesn't support hashed aggregation with DISTINCT or ORDER
* BY aggregates. (Doing so would imply storing *all* the input
* values in the hash table, and/or running many sorts in parallel,
* either of which seems like a certain loser.) We similarly don't
* support ordered-set aggregates in hashed aggregation, but that case
* is also included in the numOrderedAggs count.
*
* Note: grouping_is_hashable() is much more expensive to check than
* the other gating conditions, so we want to do it last.
*/
if ((parse->groupClause != NIL &&
agg_costs->numOrderedAggs == 0 &&
(gd ? gd->any_hashable : grouping_is_hashable(parse->groupClause))))
flags |= GROUPING_CAN_USE_HASH;
/*
* Determine whether partial aggregation is possible.
*/
if (can_partial_agg(root, agg_costs))
flags |= GROUPING_CAN_PARTIAL_AGG;
create_ordinary_grouping_paths(root, input_rel, target, grouped_rel, create_ordinary_grouping_paths(root, input_rel, target, grouped_rel,
agg_costs, gd); agg_costs, gd, flags);
}
set_cheapest(grouped_rel); set_cheapest(grouped_rel);
return grouped_rel; return grouped_rel;
...@@ -3820,15 +3889,15 @@ static void ...@@ -3820,15 +3889,15 @@ static void
create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
PathTarget *target, RelOptInfo *grouped_rel, PathTarget *target, RelOptInfo *grouped_rel,
const AggClauseCosts *agg_costs, const AggClauseCosts *agg_costs,
grouping_sets_data *gd) grouping_sets_data *gd, int flags)
{ {
Query *parse = root->parse; Query *parse = root->parse;
Path *cheapest_path = input_rel->cheapest_total_path; Path *cheapest_path = input_rel->cheapest_total_path;
RelOptInfo *partially_grouped_rel = NULL; RelOptInfo *partially_grouped_rel = NULL;
AggClauseCosts agg_final_costs; /* parallel only */ AggClauseCosts agg_final_costs; /* parallel only */
double dNumGroups; double dNumGroups;
bool can_hash; bool can_hash = (flags & GROUPING_CAN_USE_HASH) != 0;
bool can_sort; bool can_sort = (flags & GROUPING_CAN_USE_SORT) != 0;
/* /*
* Estimate number of groups. * Estimate number of groups.
...@@ -3838,50 +3907,14 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, ...@@ -3838,50 +3907,14 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
gd, gd,
parse->targetList); parse->targetList);
/*
* Determine whether it's possible to perform sort-based implementations
* of grouping. (Note that if groupClause is empty,
* grouping_is_sortable() is trivially true, and all the
* pathkeys_contained_in() tests will succeed too, so that we'll consider
* every surviving input path.)
*
* If we have grouping sets, we might be able to sort some but not all of
* them; in this case, we need can_sort to be true as long as we must
* consider any sorted-input plan.
*/
can_sort = (gd && gd->rollups != NIL)
|| grouping_is_sortable(parse->groupClause);
/*
* Determine whether we should consider hash-based implementations of
* grouping.
*
* Hashed aggregation only applies if we're grouping. If we have grouping
* sets, some groups might be hashable but others not; in this case we set
* can_hash true as long as there is nothing globally preventing us from
* hashing (and we should therefore consider plans with hashes).
*
* Executor doesn't support hashed aggregation with DISTINCT or ORDER BY
* aggregates. (Doing so would imply storing *all* the input values in
* the hash table, and/or running many sorts in parallel, either of which
* seems like a certain loser.) We similarly don't support ordered-set
* aggregates in hashed aggregation, but that case is also included in the
* numOrderedAggs count.
*
* Note: grouping_is_hashable() is much more expensive to check than the
* other gating conditions, so we want to do it last.
*/
can_hash = (parse->groupClause != NIL &&
agg_costs->numOrderedAggs == 0 &&
(gd ? gd->any_hashable : grouping_is_hashable(parse->groupClause)));
/* /*
* Before generating paths for grouped_rel, we first generate any possible * Before generating paths for grouped_rel, we first generate any possible
* partially grouped paths; that way, later code can easily consider both * partially grouped paths; that way, later code can easily consider both
* parallel and non-parallel approaches to grouping. * parallel and non-parallel approaches to grouping.
*/ */
MemSet(&agg_final_costs, 0, sizeof(AggClauseCosts)); MemSet(&agg_final_costs, 0, sizeof(AggClauseCosts));
if (can_parallel_agg(root, input_rel, grouped_rel, agg_costs)) if (grouped_rel->consider_parallel && input_rel->partial_pathlist != NIL
&& (flags & GROUPING_CAN_PARTIAL_AGG) != 0)
{ {
partially_grouped_rel = partially_grouped_rel =
create_partial_grouping_paths(root, create_partial_grouping_paths(root,
...@@ -6490,28 +6523,17 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel) ...@@ -6490,28 +6523,17 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
} }
/* /*
* can_parallel_agg * can_partial_agg
* *
* Determines whether or not parallel grouping and/or aggregation is possible. * Determines whether or not partial grouping and/or aggregation is possible.
* Returns true when possible, false otherwise. * Returns true when possible, false otherwise.
*/ */
static bool static bool
can_parallel_agg(PlannerInfo *root, RelOptInfo *input_rel, can_partial_agg(PlannerInfo *root, const AggClauseCosts *agg_costs)
RelOptInfo *grouped_rel, const AggClauseCosts *agg_costs)
{ {
Query *parse = root->parse; Query *parse = root->parse;
if (!grouped_rel->consider_parallel) if (!parse->hasAggs && parse->groupClause == NIL)
{
/* Not even parallel-safe. */
return false;
}
else if (input_rel->partial_pathlist == NIL)
{
/* Nothing to use as input for partial aggregate. */
return false;
}
else if (!parse->hasAggs && parse->groupClause == NIL)
{ {
/* /*
* We don't know how to do parallel aggregation unless we have either * We don't know how to do parallel aggregation unless we have either
......
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