diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index cc208a6a9bb3f568675c5f91d9c5e231a0f2e1b0..070ad316eb7ca694d92f70c928346211c34ee1a3 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 40c39772649503a3175c1fe669fd728639d49c3b..1435f052fa82d947271672d67cf567e16ecf3d37 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 b5f96839755896a3a1cef14808843476df9c65eb..0b5cb9e8682c218b49c9c945de7a55a5b70ad0a3 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 526126df6fc8cf67a78975c92a250e8582ab8b5c..be7c639f7b93a6b21de532748f98b886960474d8 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);