0001-Refactor-partitonwise-aggregate-signalling.patch
application/octet-stream
Filename: 0001-Refactor-partitonwise-aggregate-signalling.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch 0001
Subject: Refactor partitonwise aggregate signalling.
| File | + | − |
|---|---|---|
| src/backend/optimizer/plan/planner.c | 85 | 130 |
| src/include/nodes/relation.h | 20 | 6 |
From 71bb618f69872623366ca85ff4799c99c7ca9e1f Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Wed, 21 Mar 2018 17:34:17 -0400
Subject: [PATCH] Refactor partitonwise aggregate signalling.
---
src/backend/optimizer/plan/planner.c | 215 ++++++++++++++---------------------
src/include/nodes/relation.h | 26 ++++-
2 files changed, 105 insertions(+), 136 deletions(-)
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index f2b1a8bf39..e3d4dcaae7 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -107,14 +107,10 @@ typedef struct
* 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.
- *
- * GROUPING_CAN_PARTITIONWISE_AGG should be set if it's possible to perform
- * partitionwise grouping and/or aggregation.
*/
#define GROUPING_CAN_USE_SORT 0x0001
#define GROUPING_CAN_USE_HASH 0x0002
#define GROUPING_CAN_PARTIAL_AGG 0x0004
-#define GROUPING_CAN_PARTITIONWISE_AGG 0x0008
/*
* Data specific to grouping sets
@@ -237,7 +233,8 @@ static RelOptInfo *create_partial_grouping_paths(PlannerInfo *root,
RelOptInfo *grouped_rel,
RelOptInfo *input_rel,
grouping_sets_data *gd,
- GroupPathExtraData *extra);
+ GroupPathExtraData *extra,
+ bool force_rel_creation);
static void gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel);
static bool can_partial_agg(PlannerInfo *root,
const AggClauseCosts *agg_costs);
@@ -246,18 +243,13 @@ static void apply_scanjoin_target_to_paths(PlannerInfo *root,
PathTarget *scanjoin_target,
bool scanjoin_target_parallel_safe,
bool modify_in_place);
-static bool can_partitionwise_grouping(PlannerInfo *root,
- RelOptInfo *input_rel,
- RelOptInfo *grouped_rel,
- GroupPathExtraData *extra,
- grouping_sets_data *gd,
- bool *perform_partial_partitionwise_aggregation);
static void create_partitionwise_grouping_paths(PlannerInfo *root,
RelOptInfo *input_rel,
RelOptInfo *grouped_rel,
RelOptInfo *partially_grouped_rel,
const AggClauseCosts *agg_costs,
grouping_sets_data *gd,
+ PartitionwiseAggregateType patype,
GroupPathExtraData *extra);
static bool group_by_has_partkey(RelOptInfo *input_rel,
RelOptInfo *grouped_rel,
@@ -3742,16 +3734,17 @@ create_grouping_paths(PlannerInfo *root,
extra.havingQual = parse->havingQual;
extra.targetList = parse->targetList;
extra.partial_costs_set = false;
- extra.is_partial_aggregation = false;
/*
- * Check whether we can perform partitionwise grouping and/or
- * aggregation.
+ * Determine whether partitionwise aggregation is in theory possible.
+ * It can be disabled by the user, and for now, we don't try to
+ * support grouping sets. create_ordinary_grouping_paths() will check
+ * additional conditions, such as whether input_rel is partitioned.
*/
- if (can_partitionwise_grouping(root, input_rel, grouped_rel, &extra,
- gd,
- &extra.perform_partial_partitionwise_aggregation))
- extra.flags |= GROUPING_CAN_PARTITIONWISE_AGG;
+ if (enable_partitionwise_aggregate && !parse->groupingSets)
+ extra.patype = PARTITIONWISE_AGGREGATE_FULL;
+ else
+ extra.patype = PARTITIONWISE_AGGREGATE_NONE;
create_ordinary_grouping_paths(root, input_rel, grouped_rel,
agg_costs, gd, &extra,
@@ -3923,6 +3916,38 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
Path *cheapest_path = input_rel->cheapest_total_path;
RelOptInfo *partially_grouped_rel = NULL;
double dNumGroups;
+ PartitionwiseAggregateType patype = PARTITIONWISE_AGGREGATE_NONE;
+
+ /*
+ * If this is the topmost grouping relation or if the parent relation is
+ * doing some form of partitionwise aggregation, then we may be able to do
+ * it at this level also. However, if the input relation is not
+ * partitioned, partition-wise aggregate is impossible, and if it is dummy,
+ * partition-wise aggregate is pointless.
+ */
+ if (extra->patype != PARTITIONWISE_AGGREGATE_NONE &&
+ input_rel->part_scheme && input_rel->part_rels &&
+ !IS_DUMMY_REL(input_rel))
+ {
+ /*
+ * If this is the topmost relation or if the parent relation is doing
+ * full partitionwise aggregation, then we can do full partitionwise
+ * aggregation provided that the GROUP BY clause contains all of the
+ * partitioning columns at this level. Otherwise, we can do at most
+ * partial partitionwise aggregation. But if partial aggregation is
+ * not supported in general then we can't use it for partitionwise
+ * aggregation either.
+ */
+ if (extra->patype == PARTITIONWISE_AGGREGATE_FULL &&
+ group_by_has_partkey(input_rel, grouped_rel,
+ extra->targetList,
+ root->parse->groupClause))
+ patype = PARTITIONWISE_AGGREGATE_FULL;
+ else if ((extra->flags & GROUPING_CAN_PARTIAL_AGG) != 0)
+ patype = PARTITIONWISE_AGGREGATE_PARTIAL;
+ else
+ patype = PARTITIONWISE_AGGREGATE_NONE;
+ }
/*
* Before generating paths for grouped_rel, we first generate any possible
@@ -3930,12 +3955,24 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
* parallel and non-parallel approaches to grouping.
*/
if ((extra->flags & GROUPING_CAN_PARTIAL_AGG) != 0)
+ {
+ bool force_rel_creation;
+
+ /*
+ * If we're doing partition-wise aggregation at this level, force
+ * creation of a partially_grouped_rel so we can add partition-wise
+ * paths to it.
+ */
+ force_rel_creation = (patype == PARTITIONWISE_AGGREGATE_PARTIAL);
+
partially_grouped_rel =
create_partial_grouping_paths(root,
grouped_rel,
input_rel,
gd,
- extra);
+ extra,
+ force_rel_creation);
+ }
/*
* Set partially_grouped_rel_p so that the caller get the newly created
@@ -3944,13 +3981,13 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
*partially_grouped_rel_p = partially_grouped_rel;
/* Apply partitionwise aggregation technique, if possible. */
- if ((extra->flags & GROUPING_CAN_PARTITIONWISE_AGG) != 0)
+ if (patype != PARTITIONWISE_AGGREGATE_NONE)
create_partitionwise_grouping_paths(root, input_rel, grouped_rel,
partially_grouped_rel, agg_costs,
- gd, extra);
+ gd, patype, extra);
/* If we are doing partial aggregation only, return. */
- if (extra->is_partial_aggregation)
+ if (extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL)
{
Assert(partially_grouped_rel);
@@ -6393,7 +6430,8 @@ create_partial_grouping_paths(PlannerInfo *root,
RelOptInfo *grouped_rel,
RelOptInfo *input_rel,
grouping_sets_data *gd,
- GroupPathExtraData *extra)
+ GroupPathExtraData *extra,
+ bool force_rel_creation)
{
Query *parse = root->parse;
RelOptInfo *partially_grouped_rel;
@@ -6409,11 +6447,13 @@ create_partial_grouping_paths(PlannerInfo *root,
/*
* Consider whether we should generate partially aggregated non-partial
- * paths. We can only do this if we have a non-partial path, and in
- * addition the caller must have requested it by setting
- * extra->is_partial_aggregation.
+ * paths. We can only do this if we have a non-partial path, and only if
+ * the parent of the input rel is performing partial partitionwise
+ * aggregation. (Note that extra->patype is the type of partitionwise
+ * aggregation being used at the parent level, not this level.)
*/
- if (input_rel->pathlist != NIL && extra->is_partial_aggregation)
+ if (input_rel->pathlist != NIL &&
+ extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL)
cheapest_total_path = input_rel->cheapest_total_path;
/*
@@ -6426,16 +6466,12 @@ create_partial_grouping_paths(PlannerInfo *root,
/*
* If we can't partially aggregate partial paths, and we can't partially
- * aggregate non-partial paths, then there may not be any point to
- * creating a new RelOptInfo after all. However, if partitionwise
- * aggregate is a possibility and going to perform a partial aggregation,
- * then we need to create the RelOptInfo anyway, because the caller may
- * want to add Append paths to it.
+ * aggregate non-partial paths, then don't bother creating the new
+ * RelOptInfo at all, unless the caller specified force_rel_creation.
*/
if (cheapest_total_path == NULL &&
cheapest_partial_path == NULL &&
- ((extra->flags & GROUPING_CAN_PARTITIONWISE_AGG) == 0 ||
- !extra->perform_partial_partitionwise_aggregation))
+ !force_rel_creation)
return NULL;
/*
@@ -6855,77 +6891,6 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
}
}
-/*
- * can_partitionwise_grouping
- *
- * Can we use partitionwise grouping technique?
- */
-static bool
-can_partitionwise_grouping(PlannerInfo *root,
- RelOptInfo *input_rel,
- RelOptInfo *grouped_rel,
- GroupPathExtraData *extra,
- grouping_sets_data *gd,
- bool *perform_partial_partitionwise_aggregation)
-{
- Query *parse = root->parse;
-
- *perform_partial_partitionwise_aggregation = false;
-
- /* No, if user disabled partitionwise aggregation. */
- if (!enable_partitionwise_aggregate)
- return false;
-
- /*
- * Currently, grouping sets plan does not work with an inheritance subtree
- * (see notes in create_groupingsets_plan). Moreover, grouping sets
- * implies multiple group by clauses, each of which may not have all
- * partition keys. Those sets which have all partition keys will be
- * computed completely for each partition, but others will require partial
- * aggregation. We will need to apply partitionwise aggregation at each
- * derived group by clause and not as a whole-sale strategy. Due to this
- * we won't be able to compute "whole" grouping sets here and thus bail
- * out.
- */
- if (parse->groupingSets || gd)
- return false;
-
- /*
- * Nothing to do, if the input relation is not partitioned or it has no
- * partitioned relations.
- */
- if (!input_rel->part_scheme || !input_rel->part_rels)
- return false;
-
- /* Nothing to do, if the input relation itself is dummy. */
- if (IS_DUMMY_REL(input_rel))
- return false;
-
- /*
- * If partition keys are part of group by clauses, then we can do full
- * partitionwise aggregation. Otherwise need to calculate partial
- * aggregates for each partition and combine them.
- *
- * However, if caller forces to perform partial aggregation, then do that
- * unconditionally.
- */
- *perform_partial_partitionwise_aggregation = (extra->is_partial_aggregation ||
- !group_by_has_partkey(input_rel,
- grouped_rel,
- extra->targetList,
- parse->groupClause));
-
- /*
- * If we need to perform partial aggregation for every child but cannot
- * compute partial aggregates, no partitionwise grouping is possible.
- */
- if (*perform_partial_partitionwise_aggregation &&
- (extra->flags & GROUPING_CAN_PARTIAL_AGG) == 0)
- return false;
-
- return true;
-}
-
/*
* create_partitionwise_grouping_paths
*
@@ -6951,25 +6916,22 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
RelOptInfo *partially_grouped_rel,
const AggClauseCosts *agg_costs,
grouping_sets_data *gd,
+ PartitionwiseAggregateType patype,
GroupPathExtraData *extra)
{
- int nparts;
+ int nparts = input_rel->nparts;
int cnt_parts;
RelOptInfo **part_rels;
List *grouped_live_children = NIL;
List *partially_grouped_live_children = NIL;
PathTarget *target = extra->target;
- nparts = input_rel->nparts;
- part_rels = (RelOptInfo **) palloc(nparts * sizeof(RelOptInfo *));
-
- /*
- * If partial partitionwise aggregation needs to be performed, then we
- * must have created a partially_grouped_rel already.
- */
- Assert(!extra->perform_partial_partitionwise_aggregation ||
+ Assert(patype != PARTITIONWISE_AGGREGATE_NONE);
+ Assert(patype != PARTITIONWISE_AGGREGATE_PARTIAL ||
partially_grouped_rel != NULL);
+ part_rels = (RelOptInfo **) palloc(nparts * sizeof(RelOptInfo *));
+
/*
* For full aggregation or grouping, each partition produces a disjoint
* groups which can simply be appended and thus we can say that the child
@@ -6977,7 +6939,7 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
* the partitioning details for this grouped rel. In case of a partial
* aggregation, this is not true.
*/
- if (!extra->perform_partial_partitionwise_aggregation)
+ if (patype == PARTITIONWISE_AGGREGATE_FULL)
{
grouped_rel->part_scheme = input_rel->part_scheme;
grouped_rel->nparts = nparts;
@@ -7026,8 +6988,12 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
nappinfos,
appinfos);
- /* Is partially aggregated result expected from every child? */
- child_extra.is_partial_aggregation = extra->perform_partial_partitionwise_aggregation;
+ /*
+ * extra->patype was the value computed for our parent rel; patype
+ * is the value for this relation. For the child, our value is it's
+ * parent rel's value.
+ */
+ child_extra.patype = patype;
/*
* Create grouping relation to hold fully aggregated grouping and/or
@@ -7046,17 +7012,6 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
continue;
}
- /*
- * Check whether we can perform partitionwise grouping and/or
- * aggregation on this child grouped rel.
- */
- if (can_partitionwise_grouping(root, child_input_rel,
- child_grouped_rel, &child_extra, gd,
- &child_extra.perform_partial_partitionwise_aggregation))
- child_extra.flags |= GROUPING_CAN_PARTITIONWISE_AGG;
- else
- child_extra.flags &= ~GROUPING_CAN_PARTITIONWISE_AGG;
-
/*
* Copy pathtarget from underneath scan/join as we are modifying it
* and translate its Vars with respect to this appendrel. We use
@@ -7090,7 +7045,7 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
child_partially_grouped_rel);
}
- if (!extra->perform_partial_partitionwise_aggregation)
+ if (patype == PARTITIONWISE_AGGREGATE_FULL)
{
part_rels[cnt_parts] = child_grouped_rel;
grouped_live_children = lappend(grouped_live_children,
@@ -7134,7 +7089,7 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
* Now, create append rel for all grouped children and stick them into the
* grouped_rel.
*/
- if (!extra->perform_partial_partitionwise_aggregation)
+ if (patype == PARTITIONWISE_AGGREGATE_FULL)
add_paths_to_append_rel(root, grouped_rel, grouped_live_children);
}
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index a920b30def..f2883de1f2 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -2295,6 +2295,24 @@ typedef struct JoinPathExtraData
Relids param_source_rels;
} JoinPathExtraData;
+/*
+ * What kind of partitionwise aggregation is in use?
+ *
+ * PARTITIONWISE_AGGREGATE_NONE: Not used.
+ *
+ * PARTITIONWISE_AGGREGATE_FULL: Aggregate each partition separately, and
+ * append the results.
+ *
+ * PARTITIONWISE_AGGREGATE_PARTIAL: Partially aggregate each partition
+ * separately, append the results, and then finalize aggregation.
+ */
+typedef enum
+{
+ PARTITIONWISE_AGGREGATE_NONE,
+ PARTITIONWISE_AGGREGATE_FULL,
+ PARTITIONWISE_AGGREGATE_PARTIAL
+} PartitionwiseAggregateType;
+
/*
* Struct for extra information passed to subroutines of create_grouping_paths
*
@@ -2307,9 +2325,7 @@ typedef struct JoinPathExtraData
* target_parallel_safe is true if target is parallel safe.
* havingQual gives list of quals to be applied post aggregation.
* targetList gives list of columns to be projected.
- * is_partial_aggregation is true if doing partial aggregation.
- * perform_partial_partitionwise_aggregation is true if child needs to perform
- * partial partitionwise aggregation.
+ * patype is the type of partitionwise aggregation that is being performed.
*/
typedef struct
{
@@ -2327,9 +2343,7 @@ typedef struct
bool target_parallel_safe;
Node *havingQual;
List *targetList;
-
- bool is_partial_aggregation;
- bool perform_partial_partitionwise_aggregation;
+ PartitionwiseAggregateType patype;
} GroupPathExtraData;
/*
--
2.14.3 (Apple Git-98)