Commit f1993038 authored by Tom Lane's avatar Tom Lane

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.
parent 19e972d5
...@@ -110,8 +110,10 @@ static double get_number_of_groups(PlannerInfo *root, ...@@ -110,8 +110,10 @@ static double get_number_of_groups(PlannerInfo *root,
List *rollup_groupclauses); List *rollup_groupclauses);
static void set_grouped_rel_consider_parallel(PlannerInfo *root, static void set_grouped_rel_consider_parallel(PlannerInfo *root,
RelOptInfo *grouped_rel, RelOptInfo *grouped_rel,
PathTarget *target); PathTarget *target,
static Size estimate_hashagg_tablesize(Path *path, AggClauseCosts *agg_costs, const AggClauseCosts *agg_costs);
static Size estimate_hashagg_tablesize(Path *path,
const AggClauseCosts *agg_costs,
double dNumGroups); double dNumGroups);
static RelOptInfo *create_grouping_paths(PlannerInfo *root, static RelOptInfo *create_grouping_paths(PlannerInfo *root,
RelOptInfo *input_rel, RelOptInfo *input_rel,
...@@ -3207,7 +3209,8 @@ get_number_of_groups(PlannerInfo *root, ...@@ -3207,7 +3209,8 @@ get_number_of_groups(PlannerInfo *root,
*/ */
static void static void
set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel, set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel,
PathTarget *target) PathTarget *target,
const AggClauseCosts *agg_costs)
{ {
Query *parse = root->parse; Query *parse = root->parse;
...@@ -3240,15 +3243,14 @@ set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel, ...@@ -3240,15 +3243,14 @@ set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel,
return; return;
/* /*
* All that's left to check now is to make sure all aggregate functions * If we have any non-partial-capable aggregates, or if any of them can't
* support partial mode. If there's no aggregates then we can skip * be serialized, we can't go parallel.
* checking that.
*/ */
if (!parse->hasAggs) if (agg_costs->hasNonPartial || agg_costs->hasNonSerial)
grouped_rel->consider_parallel = true; return;
else if (aggregates_allow_partial((Node *) target->exprs) == PAT_ANY &&
aggregates_allow_partial(root->parse->havingQual) == PAT_ANY) /* OK, consider parallelization */
grouped_rel->consider_parallel = true; grouped_rel->consider_parallel = true;
} }
/* /*
...@@ -3257,7 +3259,7 @@ set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel, ...@@ -3257,7 +3259,7 @@ set_grouped_rel_consider_parallel(PlannerInfo *root, RelOptInfo *grouped_rel,
* require based on the agg_costs, path width and dNumGroups. * require based on the agg_costs, path width and dNumGroups.
*/ */
static Size static Size
estimate_hashagg_tablesize(Path *path, AggClauseCosts *agg_costs, estimate_hashagg_tablesize(Path *path, const AggClauseCosts *agg_costs,
double dNumGroups) double dNumGroups)
{ {
Size hashentrysize; Size hashentrysize;
...@@ -3411,7 +3413,8 @@ create_grouping_paths(PlannerInfo *root, ...@@ -3411,7 +3413,8 @@ create_grouping_paths(PlannerInfo *root,
* going to be safe to do so. * going to be safe to do so.
*/ */
if (input_rel->partial_pathlist != NIL) 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 * Determine whether it's possible to perform sort-based implementations
......
...@@ -52,10 +52,6 @@ ...@@ -52,10 +52,6 @@
#include "utils/syscache.h" #include "utils/syscache.h"
#include "utils/typcache.h" #include "utils/typcache.h"
typedef struct
{
PartialAggType allowedtype;
} partial_agg_context;
typedef struct typedef struct
{ {
...@@ -98,8 +94,6 @@ typedef struct ...@@ -98,8 +94,6 @@ typedef struct
bool allow_restricted; bool allow_restricted;
} has_parallel_hazard_arg; } 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 contain_agg_clause_walker(Node *node, void *context);
static bool get_agg_clause_costs_walker(Node *node, static bool get_agg_clause_costs_walker(Node *node,
get_agg_clause_costs_context *context); get_agg_clause_costs_context *context);
...@@ -403,81 +397,6 @@ make_ands_implicit(Expr *clause) ...@@ -403,81 +397,6 @@ make_ands_implicit(Expr *clause)
* Aggregate-function clause manipulation * 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 * contain_agg_clause
* Recursively search for Aggref/GroupingFunc nodes within a clause. * Recursively search for Aggref/GroupingFunc nodes within a clause.
...@@ -529,8 +448,9 @@ contain_agg_clause_walker(Node *node, void *context) ...@@ -529,8 +448,9 @@ contain_agg_clause_walker(Node *node, void *context)
* *
* We count the nodes, estimate their execution costs, and estimate the total * We count the nodes, estimate their execution costs, and estimate the total
* space needed for their transition state values if all are evaluated in * space needed for their transition state values if all are evaluated in
* parallel (as would be done in a HashAgg plan). See AggClauseCosts for * parallel (as would be done in a HashAgg plan). Also, we check whether
* the exact set of statistics collected. * partial aggregation is feasible. See AggClauseCosts for the exact set
* of statistics collected.
* *
* In addition, we mark Aggref nodes with the correct aggtranstype, so * 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 * 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) ...@@ -616,10 +536,40 @@ get_agg_clause_costs_walker(Node *node, get_agg_clause_costs_context *context)
aggref->aggtranstype = aggtranstype; 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++; costs->numAggs++;
if (aggref->aggorder != NIL || aggref->aggdistinct != NIL) if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
{
costs->numOrderedAggs++; 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 * Add the appropriate component function execution costs to
......
...@@ -50,12 +50,15 @@ typedef struct QualCost ...@@ -50,12 +50,15 @@ typedef struct QualCost
* Costing aggregate function execution requires these statistics about * Costing aggregate function execution requires these statistics about
* the aggregates to be executed by a given Agg node. Note that the costs * the aggregates to be executed by a given Agg node. Note that the costs
* include the execution costs of the aggregates' argument expressions as * 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 typedef struct AggClauseCosts
{ {
int numAggs; /* total number of aggregate functions */ int numAggs; /* total number of aggregate functions */
int numOrderedAggs; /* number w/ DISTINCT/ORDER BY/WITHIN GROUP */ 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 */ QualCost transCost; /* total per-input-row execution costs */
Cost finalCost; /* total per-aggregated-row costs */ Cost finalCost; /* total per-aggregated-row costs */
Size transitionSpace; /* space for pass-by-ref transition data */ Size transitionSpace; /* space for pass-by-ref transition data */
......
...@@ -27,25 +27,6 @@ typedef struct ...@@ -27,25 +27,6 @@ typedef struct
List **windowFuncs; /* lists of WindowFuncs for each winref */ List **windowFuncs; /* lists of WindowFuncs for each winref */
} WindowFuncLists; } 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, extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
Expr *leftop, Expr *rightop, Expr *leftop, Expr *rightop,
Oid opcollid, Oid inputcollid); Oid opcollid, Oid inputcollid);
...@@ -65,7 +46,6 @@ extern Node *make_and_qual(Node *qual1, Node *qual2); ...@@ -65,7 +46,6 @@ extern Node *make_and_qual(Node *qual1, Node *qual2);
extern Expr *make_ands_explicit(List *andclauses); extern Expr *make_ands_explicit(List *andclauses);
extern List *make_ands_implicit(Expr *clause); extern List *make_ands_implicit(Expr *clause);
extern PartialAggType aggregates_allow_partial(Node *clause);
extern bool contain_agg_clause(Node *clause); extern bool contain_agg_clause(Node *clause);
extern void get_agg_clause_costs(PlannerInfo *root, Node *clause, extern void get_agg_clause_costs(PlannerInfo *root, Node *clause,
AggSplit aggsplit, AggClauseCosts *costs); AggSplit aggsplit, AggClauseCosts *costs);
......
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