]> granicus.if.org Git - postgresql/commitdiff
Avoid making a separate pass over the query to check for partializability.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 26 Jun 2016 19:55:01 +0000 (15:55 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 26 Jun 2016 19:55:01 +0000 (15:55 -0400)
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.

src/backend/optimizer/plan/planner.c
src/backend/optimizer/util/clauses.c
src/include/nodes/relation.h
src/include/optimizer/clauses.h

index cc208a6a9bb3f568675c5f91d9c5e231a0f2e1b0..070ad316eb7ca694d92f70c928346211c34ee1a3 100644 (file)
@@ -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
index 40c39772649503a3175c1fe669fd728639d49c3b..1435f052fa82d947271672d67cf567e16ecf3d37 100644 (file)
 #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
index b5f96839755896a3a1cef14808843476df9c65eb..0b5cb9e8682c218b49c9c945de7a55a5b70ad0a3 100644 (file)
@@ -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 */
index 526126df6fc8cf67a78975c92a250e8582ab8b5c..be7c639f7b93a6b21de532748f98b886960474d8 100644 (file)
@@ -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);