mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
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
This commit is contained in:
parent
4f15e5d09d
commit
b5996c2791
@ -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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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
|
|||||||
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,
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user