aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-06-26 15:55:01 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2016-06-26 15:55:01 -0400
commitf1993038a4f0ce5fbeb7b562b2acd571bf6b567b (patch)
tree93f5ee92beb05cab27515290246a72a1b6648845 /src
parent19e972d5580c655423572e3c870e47b5b7c346f6 (diff)
downloadpostgresql-f1993038a4f0ce5fbeb7b562b2acd571bf6b567b.tar.gz
postgresql-f1993038a4f0ce5fbeb7b562b2acd571bf6b567b.zip
Avoid making a separate pass over the query to check for partializability.
It's rather silly to make a separate pass over the tlist + HAVING qual, and a separate set of visits to the syscache, when get_agg_clause_costs already has all the required information in hand. This nets out as less code as well as fewer cycles.
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/plan/planner.c29
-rw-r--r--src/backend/optimizer/util/clauses.c118
-rw-r--r--src/include/nodes/relation.h5
-rw-r--r--src/include/optimizer/clauses.h20
4 files changed, 54 insertions, 118 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index cc208a6a9bb..070ad316eb7 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -110,8 +110,10 @@ static double get_number_of_groups(PlannerInfo *root,
List *rollup_groupclauses);
static void set_grouped_rel_consider_parallel(PlannerInfo *root,
RelOptInfo *grouped_rel,
- PathTarget *target);
-static Size estimate_hashagg_tablesize(Path *path, AggClauseCosts *agg_costs,
+ PathTarget *target,
+ const AggClauseCosts *agg_costs);
+static Size estimate_hashagg_tablesize(Path *path,
+ const AggClauseCosts *agg_costs,
double dNumGroups);
static RelOptInfo *create_grouping_paths(PlannerInfo *root,
RelOptInfo *input_rel,
@@ -3207,7 +3209,8 @@ get_number_of_groups(PlannerInfo *root,
*/
static void
set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel,
- PathTarget *target)
+ PathTarget *target,
+ const AggClauseCosts *agg_costs)
{
Query *parse = root->parse;
@@ -3240,15 +3243,14 @@ set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel,
return;
/*
- * All that's left to check now is to make sure all aggregate functions
- * support partial mode. If there's no aggregates then we can skip
- * checking that.
+ * If we have any non-partial-capable aggregates, or if any of them can't
+ * be serialized, we can't go parallel.
*/
- if (!parse->hasAggs)
- grouped_rel->consider_parallel = true;
- else if (aggregates_allow_partial((Node *) target->exprs) == PAT_ANY &&
- aggregates_allow_partial(root->parse->havingQual) == PAT_ANY)
- grouped_rel->consider_parallel = true;
+ if (agg_costs->hasNonPartial || agg_costs->hasNonSerial)
+ return;
+
+ /* OK, consider parallelization */
+ grouped_rel->consider_parallel = true;
}
/*
@@ -3257,7 +3259,7 @@ set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel,
* require based on the agg_costs, path width and dNumGroups.
*/
static Size
-estimate_hashagg_tablesize(Path *path, AggClauseCosts *agg_costs,
+estimate_hashagg_tablesize(Path *path, const AggClauseCosts *agg_costs,
double dNumGroups)
{
Size hashentrysize;
@@ -3411,7 +3413,8 @@ create_grouping_paths(PlannerInfo *root,
* going to be safe to do so.
*/
if (input_rel->partial_pathlist != NIL)
- set_grouped_rel_consider_parallel(root, grouped_rel, target);
+ set_grouped_rel_consider_parallel(root, grouped_rel,
+ target, &agg_costs);
/*
* Determine whether it's possible to perform sort-based implementations
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 40c39772649..1435f052fa8 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -52,10 +52,6 @@
#include "utils/syscache.h"
#include "utils/typcache.h"
-typedef struct
-{
- PartialAggType allowedtype;
-} partial_agg_context;
typedef struct
{
@@ -98,8 +94,6 @@ typedef struct
bool allow_restricted;
} has_parallel_hazard_arg;
-static bool aggregates_allow_partial_walker(Node *node,
- partial_agg_context *context);
static bool contain_agg_clause_walker(Node *node, void *context);
static bool get_agg_clause_costs_walker(Node *node,
get_agg_clause_costs_context *context);
@@ -404,81 +398,6 @@ make_ands_implicit(Expr *clause)
*****************************************************************************/
/*
- * aggregates_allow_partial
- * Recursively search for Aggref clauses and determine the maximum
- * level of partial aggregation which can be supported.
- */
-PartialAggType
-aggregates_allow_partial(Node *clause)
-{
- partial_agg_context context;
-
- /* initially any type is okay, until we find Aggrefs which say otherwise */
- context.allowedtype = PAT_ANY;
-
- if (!aggregates_allow_partial_walker(clause, &context))
- return context.allowedtype;
- return context.allowedtype;
-}
-
-static bool
-aggregates_allow_partial_walker(Node *node, partial_agg_context *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
- {
- Aggref *aggref = (Aggref *) node;
- HeapTuple aggTuple;
- Form_pg_aggregate aggform;
-
- Assert(aggref->agglevelsup == 0);
-
- /*
- * We can't perform partial aggregation with Aggrefs containing a
- * DISTINCT or ORDER BY clause.
- */
- if (aggref->aggdistinct || aggref->aggorder)
- {
- context->allowedtype = PAT_DISABLED;
- return true; /* abort search */
- }
- aggTuple = SearchSysCache1(AGGFNOID,
- ObjectIdGetDatum(aggref->aggfnoid));
- if (!HeapTupleIsValid(aggTuple))
- elog(ERROR, "cache lookup failed for aggregate %u",
- aggref->aggfnoid);
- aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
-
- /*
- * If there is no combine function, then partial aggregation is not
- * possible.
- */
- if (!OidIsValid(aggform->aggcombinefn))
- {
- ReleaseSysCache(aggTuple);
- context->allowedtype = PAT_DISABLED;
- return true; /* abort search */
- }
-
- /*
- * If we find any aggs with an internal transtype then we must check
- * whether these have serialization/deserialization functions;
- * otherwise, we set the maximum allowed type to PAT_INTERNAL_ONLY.
- */
- if (aggform->aggtranstype == INTERNALOID &&
- (!OidIsValid(aggform->aggserialfn) ||
- !OidIsValid(aggform->aggdeserialfn)))
- context->allowedtype = PAT_INTERNAL_ONLY;
-
- ReleaseSysCache(aggTuple);
- return false; /* continue searching */
- }
- return expression_tree_walker(node, aggregates_allow_partial_walker,
- (void *) context);
-}
-
-/*
* contain_agg_clause
* Recursively search for Aggref/GroupingFunc nodes within a clause.
*
@@ -529,8 +448,9 @@ contain_agg_clause_walker(Node *node, void *context)
*
* We count the nodes, estimate their execution costs, and estimate the total
* space needed for their transition state values if all are evaluated in
- * parallel (as would be done in a HashAgg plan). See AggClauseCosts for
- * the exact set of statistics collected.
+ * parallel (as would be done in a HashAgg plan). Also, we check whether
+ * partial aggregation is feasible. See AggClauseCosts for the exact set
+ * of statistics collected.
*
* In addition, we mark Aggref nodes with the correct aggtranstype, so
* that that doesn't need to be done repeatedly. (That makes this function's
@@ -616,10 +536,40 @@ get_agg_clause_costs_walker(Node *node, get_agg_clause_costs_context *context)
aggref->aggtranstype = aggtranstype;
}
- /* count it; note ordered-set aggs always have nonempty aggorder */
+ /*
+ * Count it, and check for cases requiring ordered input. Note that
+ * ordered-set aggs always have nonempty aggorder. Any ordered-input
+ * case also defeats partial aggregation.
+ */
costs->numAggs++;
if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
+ {
costs->numOrderedAggs++;
+ costs->hasNonPartial = true;
+ }
+
+ /*
+ * Check whether partial aggregation is feasible, unless we already
+ * found out that we can't do it.
+ */
+ if (!costs->hasNonPartial)
+ {
+ /*
+ * If there is no combine function, then partial aggregation is
+ * not possible.
+ */
+ if (!OidIsValid(aggcombinefn))
+ costs->hasNonPartial = true;
+
+ /*
+ * If we have any aggs with transtype INTERNAL then we must check
+ * whether they have serialization/deserialization functions; if
+ * not, we can't serialize partial-aggregation results.
+ */
+ else if (aggtranstype == INTERNALOID &&
+ (!OidIsValid(aggserialfn) || !OidIsValid(aggdeserialfn)))
+ costs->hasNonSerial = true;
+ }
/*
* Add the appropriate component function execution costs to
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index b5f96839755..0b5cb9e8682 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -50,12 +50,15 @@ typedef struct QualCost
* Costing aggregate function execution requires these statistics about
* the aggregates to be executed by a given Agg node. Note that the costs
* include the execution costs of the aggregates' argument expressions as
- * well as the aggregate functions themselves.
+ * well as the aggregate functions themselves. Also, the fields must be
+ * defined so that initializing the struct to zeroes with memset is correct.
*/
typedef struct AggClauseCosts
{
int numAggs; /* total number of aggregate functions */
int numOrderedAggs; /* number w/ DISTINCT/ORDER BY/WITHIN GROUP */
+ bool hasNonPartial; /* does any agg not support partial mode? */
+ bool hasNonSerial; /* is any partial agg non-serializable? */
QualCost transCost; /* total per-input-row execution costs */
Cost finalCost; /* total per-aggregated-row costs */
Size transitionSpace; /* space for pass-by-ref transition data */
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 526126df6fc..be7c639f7b9 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -27,25 +27,6 @@ typedef struct
List **windowFuncs; /* lists of WindowFuncs for each winref */
} WindowFuncLists;
-/*
- * PartialAggType
- * PartialAggType stores whether partial aggregation is allowed and
- * which context it is allowed in. We require three states here as there are
- * two different contexts in which partial aggregation is safe. For aggregates
- * which have an 'stype' of INTERNAL, it is okay to pass a pointer to the
- * aggregate state within a single process, since the datum is just a
- * pointer. In cases where the aggregate state must be passed between
- * different processes, for example during parallel aggregation, passing
- * pointers directly is not going to work.
- */
-typedef enum
-{
- PAT_ANY = 0, /* Any type of partial aggregation is okay. */
- PAT_INTERNAL_ONLY, /* Some aggregates support only internal mode. */
- PAT_DISABLED /* Some aggregates don't support partial mode
- * at all */
-} PartialAggType;
-
extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
Expr *leftop, Expr *rightop,
Oid opcollid, Oid inputcollid);
@@ -65,7 +46,6 @@ extern Node *make_and_qual(Node *qual1, Node *qual2);
extern Expr *make_ands_explicit(List *andclauses);
extern List *make_ands_implicit(Expr *clause);
-extern PartialAggType aggregates_allow_partial(Node *clause);
extern bool contain_agg_clause(Node *clause);
extern void get_agg_clause_costs(PlannerInfo *root, Node *clause,
AggSplit aggsplit, AggClauseCosts *costs);