From: Tom Lane Date: Sun, 26 Jun 2016 19:55:01 +0000 (-0400) Subject: Avoid making a separate pass over the query to check for partializability. X-Git-Tag: REL9_6_BETA3~102 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f1993038a4f0ce5fbeb7b562b2acd571bf6b567b;p=postgresql 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. --- diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index cc208a6a9b..070ad316eb 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 40c3977264..1435f052fa 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); @@ -403,81 +397,6 @@ make_ands_implicit(Expr *clause) * Aggregate-function clause manipulation *****************************************************************************/ -/* - * 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 b5f9683975..0b5cb9e868 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 526126df6f..be7c639f7b 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);