mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
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.
This commit is contained in:
@ -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,
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
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,
|
|||||||
* 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,
|
|||||||
* 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 @@
|
|||||||
#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
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
*
|
*
|
||||||
* 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)
|
|||||||
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
|
|||||||
* 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
|
|||||||
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);
|
|||||||
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);
|
||||||
|
Reference in New Issue
Block a user