diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 08a914e..8f08f19 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -134,7 +134,104 @@ static Plan *build_grouping_chain(PlannerInfo *root,
 					 AttrNumber *groupColIdx,
 					 AggClauseCosts *agg_costs,
 					 long numGroups,
-					 Plan *result_plan);
+					 Plan *result_plan,
+					 PartialAggType partialaggtype);
+
+static List *
+make_aggregate_tlist(PlannerInfo *root,
+					 List *tlist,
+					 AttrNumber **groupColIdx)
+{
+	Query	   *parse = root->parse;
+	List	   *sub_tlist;
+	List	   *non_group_cols;
+	List	   *non_group_vars;
+	int			numCols;
+	ListCell   *tl;
+
+	*groupColIdx = NULL;
+
+	/*
+	 * Otherwise, we must build a tlist containing all grouping columns, plus
+	 * any other Vars mentioned in the targetlist and HAVING qual.
+	 */
+	sub_tlist = NIL;
+	non_group_cols = NIL;
+
+	numCols = list_length(parse->groupClause);
+	if (numCols > 0)
+	{
+		/*
+		 * If grouping, create sub_tlist entries for all GROUP BY columns, and
+		 * make an array showing where the group columns are in the sub_tlist.
+		 *
+		 * Note: with this implementation, the array entries will always be
+		 * 1..N, but we don't want callers to assume that.
+		 */
+		AttrNumber *grpColIdx;
+
+		grpColIdx = (AttrNumber *) palloc0(sizeof(AttrNumber) * numCols);
+		*groupColIdx = grpColIdx;
+
+		foreach(tl, tlist)
+		{
+			TargetEntry *tle = (TargetEntry *) lfirst(tl);
+			int			colno;
+
+			colno = get_grouping_column_index(parse, tle);
+			if (colno >= 0)
+			{
+				/*
+				 * It's a grouping column, so add it to the result tlist and
+				 * remember its resno in grpColIdx[].
+				 */
+				TargetEntry *newtle;
+
+				newtle = makeTargetEntry((Expr *) copyObject(tle->expr),
+										 list_length(sub_tlist) + 1,
+										 NULL,
+										 tle->resjunk);
+				newtle->ressortgroupref = tle->ressortgroupref;
+				sub_tlist = lappend(sub_tlist, newtle);
+
+				Assert(grpColIdx[colno] == 0);	/* no dups expected */
+				grpColIdx[colno] = newtle->resno;
+			}
+			else
+			{
+				non_group_cols = lappend(non_group_cols, tle->expr);
+			}
+		}
+	}
+	else
+	{
+		foreach(tl, tlist)
+		{
+			TargetEntry *tle = (TargetEntry *) lfirst(tl);
+			non_group_cols = lappend(non_group_cols, tle->expr);
+		}
+	}
+
+	/*
+	 * If there's a HAVING clause, we'll need need to ensure all Aggrefs from
+	 * there are also in the targetlist
+	 */
+	if (parse->havingQual)
+		non_group_cols = lappend(non_group_cols, parse->havingQual);
+
+
+	non_group_vars = pull_var_clause((Node *) non_group_cols,
+									 PVC_INCLUDE_AGGREGATES,
+									 PVC_INCLUDE_PLACEHOLDERS);
+
+	sub_tlist = add_to_flat_tlist(sub_tlist, non_group_vars);
+
+	/* clean up cruft */
+	list_free(non_group_vars);
+	list_free(non_group_cols);
+
+	return sub_tlist;
+}
 
 /*****************************************************************************
  *
@@ -1900,6 +1997,19 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 			AttrNumber *groupColIdx = NULL;
 			bool		need_tlist_eval = true;
 			bool		need_sort_for_grouping = false;
+			PartialAggType partialaggtype;
+
+			/* Determine the level of partial aggregation we can use */
+			if (parse->groupingSets)
+				partialaggtype = PAT_DISABLED;
+			else
+			{
+				partialaggtype = aggregates_allow_partial((Node *) tlist);
+
+				if (partialaggtype != PAT_DISABLED)
+					partialaggtype = Min(partialaggtype,
+							aggregates_allow_partial(root->parse->havingQual));
+			}
 
 			result_plan = create_plan(root, best_path);
 			current_pathkeys = best_path->pathkeys;
@@ -1917,6 +2027,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 											   &groupColIdx,
 											   &need_tlist_eval);
 
+			if (partialaggtype != PAT_DISABLED)
+				need_tlist_eval = true;
+
 			/*
 			 * create_plan returns a plan with just a "flat" tlist of required
 			 * Vars.  Usually we need to insert the sub_tlist as the tlist of
@@ -1998,21 +2111,74 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 			 */
 			if (use_hashed_grouping)
 			{
-				/* Hashed aggregate plan --- no sort needed */
-				result_plan = (Plan *) make_agg(root,
-												tlist,
-												(List *) parse->havingQual,
-												AGG_HASHED,
-												&agg_costs,
-												numGroupCols,
-												groupColIdx,
-									extract_grouping_ops(parse->groupClause),
-												NIL,
-												numGroups,
-												false,
-												true,
-												false,
-												result_plan);
+				if (partialaggtype != PAT_DISABLED)
+				{
+					AttrNumber *groupColIdx;
+					List *aggtlist;
+
+					aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx);
+
+					/* Hashed aggregate plan --- no sort needed */
+					result_plan = (Plan *) make_agg(root,
+													aggtlist,
+													NIL,
+													AGG_HASHED,
+													&agg_costs,
+													numGroupCols,
+													groupColIdx,
+										extract_grouping_ops(parse->groupClause),
+													NIL,
+													numGroups,
+													false,
+													false,
+													true,
+													result_plan);
+
+					result_plan->targetlist = aggtlist;
+
+					/*
+					 * Also, account for the cost of evaluation of the sub_tlist.
+					 * See comments for add_tlist_costs_to_plan() for more info.
+					 */
+					add_tlist_costs_to_plan(root, result_plan, aggtlist);
+
+					aggtlist  = make_aggregate_tlist(root, tlist, &groupColIdx);
+
+					result_plan = (Plan *) make_agg(root,
+													aggtlist,
+													(List *) parse->havingQual,
+													AGG_HASHED,
+													&agg_costs,
+													numGroupCols,
+													groupColIdx,
+										extract_grouping_ops(parse->groupClause),
+													NIL,
+													numGroups,
+													true,
+													true,
+													true,
+													result_plan);
+					result_plan->targetlist = tlist;
+
+				}
+				else
+				{
+					/* Hashed aggregate plan --- no sort needed */
+					result_plan = (Plan *) make_agg(root,
+													tlist,
+													(List *) parse->havingQual,
+													AGG_HASHED,
+													&agg_costs,
+													numGroupCols,
+													groupColIdx,
+										extract_grouping_ops(parse->groupClause),
+													NIL,
+													numGroups,
+													false,
+													true,
+													false,
+													result_plan);
+				}
 				/* Hashed aggregation produces randomly-ordered results */
 				current_pathkeys = NIL;
 			}
@@ -2041,7 +2207,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 												   groupColIdx,
 												   &agg_costs,
 												   numGroups,
-												   result_plan);
+												   result_plan,
+												   partialaggtype);
 
 				/*
 				 * these are destroyed by build_grouping_chain, so make sure
@@ -2496,7 +2663,8 @@ build_grouping_chain(PlannerInfo *root,
 					 AttrNumber *groupColIdx,
 					 AggClauseCosts *agg_costs,
 					 long numGroups,
-					 Plan *result_plan)
+					 Plan *result_plan,
+					 PartialAggType partialaggtype)
 {
 	AttrNumber *top_grpColIdx = groupColIdx;
 	List	   *chain = NIL;
@@ -2591,21 +2759,70 @@ build_grouping_chain(PlannerInfo *root,
 		else
 			numGroupCols = list_length(parse->groupClause);
 
-		result_plan = (Plan *) make_agg(root,
-										tlist,
-										(List *) parse->havingQual,
-								 (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
-										agg_costs,
-										numGroupCols,
-										top_grpColIdx,
-										extract_grouping_ops(groupClause),
-										gsets,
-										numGroups,
-										false,
-										true,
-										false,
-										result_plan);
+		if (partialaggtype != PAT_DISABLED)
+		{
+			AttrNumber *groupColIdx;
+			List *aggtlist;
+
+			aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx);
+
+			result_plan = (Plan *) make_agg(root,
+											aggtlist,
+											NIL,
+									 (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
+											agg_costs,
+											numGroupCols,
+											groupColIdx,
+											extract_grouping_ops(groupClause),
+											gsets,
+											numGroups,
+											false,
+											false,
+											true,
+											result_plan);
+			result_plan->targetlist = aggtlist;
+
+			/*
+			 * Also, account for the cost of evaluation of the sub_tlist.
+			 * See comments for add_tlist_costs_to_plan() for more info.
+			 */
+			add_tlist_costs_to_plan(root, result_plan, aggtlist);
+
+			aggtlist  = make_aggregate_tlist(root, tlist, &groupColIdx);
 
+			result_plan = (Plan *) make_agg(root,
+											aggtlist,
+											(List *) parse->havingQual,
+									 (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
+											agg_costs,
+											numGroupCols,
+											groupColIdx,
+											extract_grouping_ops(groupClause),
+											gsets,
+											numGroups,
+											true,
+											true,
+											true,
+											result_plan);
+			result_plan->targetlist = tlist;
+		}
+		else
+		{
+			result_plan = (Plan *) make_agg(root,
+											tlist,
+											(List *) parse->havingQual,
+									 (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
+											agg_costs,
+											numGroupCols,
+											top_grpColIdx,
+											extract_grouping_ops(groupClause),
+											gsets,
+											numGroups,
+											false,
+											true,
+											false,
+											result_plan);
+		}
 		((Agg *) result_plan)->chain = chain;
 
 		/*
