diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b2dc451..5524f1b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1228,6 +1228,7 @@ _copyAggref(const Aggref *from)
 
 	COPY_SCALAR_FIELD(aggfnoid);
 	COPY_SCALAR_FIELD(aggtype);
+	COPY_SCALAR_FIELD(aggtranstype);
 	COPY_SCALAR_FIELD(aggcollid);
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(aggdirectargs);
@@ -1238,6 +1239,7 @@ _copyAggref(const Aggref *from)
 	COPY_SCALAR_FIELD(aggstar);
 	COPY_SCALAR_FIELD(aggvariadic);
 	COPY_SCALAR_FIELD(aggkind);
+	COPY_SCALAR_FIELD(aggskipfinalize);
 	COPY_SCALAR_FIELD(agglevelsup);
 	COPY_LOCATION_FIELD(location);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 356fcaf..8bcbb75 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -191,6 +191,7 @@ _equalAggref(const Aggref *a, const Aggref *b)
 {
 	COMPARE_SCALAR_FIELD(aggfnoid);
 	COMPARE_SCALAR_FIELD(aggtype);
+	COMPARE_SCALAR_FIELD(aggtranstype);
 	COMPARE_SCALAR_FIELD(aggcollid);
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(aggdirectargs);
@@ -201,6 +202,11 @@ _equalAggref(const Aggref *a, const Aggref *b)
 	COMPARE_SCALAR_FIELD(aggstar);
 	COMPARE_SCALAR_FIELD(aggvariadic);
 	COMPARE_SCALAR_FIELD(aggkind);
+
+	/* Commented out for now as this causes:
+	  "variable not found in subplan target list"
+	 COMPARE_SCALAR_FIELD(aggskipfinalize);
+	 */
 	COMPARE_SCALAR_FIELD(agglevelsup);
 	COMPARE_LOCATION_FIELD(location);
 
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index a11cb9f..5f547bd 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -57,7 +57,13 @@ exprType(const Node *expr)
 			type = ((const Param *) expr)->paramtype;
 			break;
 		case T_Aggref:
-			type = ((const Aggref *) expr)->aggtype;
+			{
+				Aggref *aggref = (Aggref *) expr;
+				if (aggref->aggskipfinalize)
+					type = aggref->aggtranstype;
+				else
+					type = aggref->aggtype;
+			}
 			break;
 		case T_GroupingFunc:
 			type = INT4OID;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6f6ccdc..6870e39 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1016,6 +1016,7 @@ _outAggref(StringInfo str, const Aggref *node)
 
 	WRITE_OID_FIELD(aggfnoid);
 	WRITE_OID_FIELD(aggtype);
+	WRITE_OID_FIELD(aggtranstype);
 	WRITE_OID_FIELD(aggcollid);
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(aggdirectargs);
@@ -1026,6 +1027,7 @@ _outAggref(StringInfo str, const Aggref *node)
 	WRITE_BOOL_FIELD(aggstar);
 	WRITE_BOOL_FIELD(aggvariadic);
 	WRITE_CHAR_FIELD(aggkind);
+	WRITE_BOOL_FIELD(aggskipfinalize);
 	WRITE_UINT_FIELD(agglevelsup);
 	WRITE_LOCATION_FIELD(location);
 }
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 67d630f..c5c8b0d 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,
+					 bool combineAggs);
+
+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,
+										 tle->resname ? pstrdup(tle->resname) : 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;
+}
 
 /*****************************************************************************
  *
@@ -1889,6 +1986,15 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 			 * results.
 			 */
 			bool		need_sort_for_grouping = false;
+			bool		combineaggs;
+
+			/*
+			 * Enable partial aggregation test if all aggregates support
+			 * partial aggregation, and we have no grouping sets
+			 */
+			combineaggs = parse->groupingSets == NIL &&
+						  aggregates_allow_partial((Node *) tlist) &&
+						  aggregates_allow_partial(root->parse->havingQual);
 
 			result_plan = create_plan(root, best_path);
 			current_pathkeys = best_path->pathkeys;
@@ -1906,6 +2012,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 				need_tlist_eval = true;
 			}
 
+			if (combineaggs)
+				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
@@ -1984,20 +2093,83 @@ 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,
-												result_plan);
+				if (combineaggs)
+				{
+					AttrNumber *groupColIdx;
+					List *aggtlist;
+
+					/* Hack hack */
+					aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx);
+					{
+						ListCell *lc;
+						foreach(lc, aggtlist)
+						{
+							TargetEntry *tle = (TargetEntry *) lfirst(lc);
+							Aggref *aggref = (Aggref *) tle->expr;
+							if (IsA(aggref, Aggref))
+								aggref->aggskipfinalize = true;
+						}
+					}
+					/* End of hack */
+
+					/* 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,
+													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,
+													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,
+													result_plan);
+				}
 				/* Hashed aggregation produces randomly-ordered results */
 				current_pathkeys = NIL;
 			}
@@ -2023,7 +2195,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 												   groupColIdx,
 												   &agg_costs,
 												   numGroups,
-												   result_plan);
+												   result_plan,
+												   combineaggs);
 
 				/*
 				 * these are destroyed by build_grouping_chain, so make sure
@@ -2477,7 +2650,8 @@ build_grouping_chain(PlannerInfo *root,
 					 AttrNumber *groupColIdx,
 					 AggClauseCosts *agg_costs,
 					 long numGroups,
-					 Plan *result_plan)
+					 Plan *result_plan,
+					 bool combineAggs)
 {
 	AttrNumber *top_grpColIdx = groupColIdx;
 	List	   *chain = NIL;
@@ -2571,20 +2745,79 @@ 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,
-										result_plan);
+		if (combineAggs)
+		{
+			AttrNumber *groupColIdx;
+			List *aggtlist;
+
+			 /* Hack hack */
+			aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx);
+			{
+				ListCell *lc;
+				foreach(lc, aggtlist)
+				{
+					TargetEntry *tle = (TargetEntry *) lfirst(lc);
+					Aggref *aggref = (Aggref *) tle->expr;
+					if (IsA(aggref, Aggref))
+						aggref->aggskipfinalize = true;
+				}
+			}
+
+			/* End of hack */
+			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,
+											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,
+											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,
+											result_plan);
+		}
 		((Agg *) result_plan)->chain = chain;
 
 		/*
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 12e9290..fb423fc 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -139,6 +139,15 @@ static List *set_returning_clause_references(PlannerInfo *root,
 static bool fix_opfuncids_walker(Node *node, void *context);
 static bool extract_query_dependencies_walker(Node *node,
 								  PlannerInfo *context);
+static void set_agg_references(PlannerInfo *root, Plan *plan, int rtoffset);
+static Node *fix_combine_agg_expr(PlannerInfo *root,
+								  Node *node,
+								  indexed_tlist *subplan_itlist,
+								  Index newvarno,
+								  int rtoffset);
+static Node *fix_combine_agg_expr_mutator(Node *node,
+										  fix_upper_expr_context *context);
+
 
 /*****************************************************************************
  *
@@ -668,7 +677,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 			}
 			break;
 		case T_Agg:
-			set_upper_references(root, plan, rtoffset);
+			set_agg_references(root, plan, rtoffset);
 			break;
 		case T_Group:
 			set_upper_references(root, plan, rtoffset);
@@ -2432,3 +2441,211 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
 	return expression_tree_walker(node, extract_query_dependencies_walker,
 								  (void *) context);
 }
+
+/*
+ * set_upper_references
+ *	  Update the targetlist and quals of an upper-level plan node
+ *	  to refer to the tuples returned by its lefttree subplan.
+ *	  Also perform opcode lookup for these expressions, and
+ *	  add regclass OIDs to root->glob->relationOids.
+ *
+ * This is used for single-input plan types like Agg, Group, Result.
+ *
+ * In most cases, we have to match up individual Vars in the tlist and
+ * qual expressions with elements of the subplan's tlist (which was
+ * generated by flatten_tlist() from these selfsame expressions, so it
+ * should have all the required variables).  There is an important exception,
+ * however: GROUP BY and ORDER BY expressions will have been pushed into the
+ * subplan tlist unflattened.  If these values are also needed in the output
+ * then we want to reference the subplan tlist element rather than recomputing
+ * the expression.
+ */
+static void
+set_agg_references(PlannerInfo *root, Plan *plan, int rtoffset)
+{
+	Agg 	   *agg = (Agg*)plan;
+	Plan	   *subplan = plan->lefttree;
+	indexed_tlist *subplan_itlist;
+	List	   *output_targetlist;
+	ListCell   *l;
+
+	subplan_itlist = build_tlist_index(subplan->targetlist);
+
+	output_targetlist = NIL;
+
+	if (agg->combineStates)
+	{
+		foreach(l, plan->targetlist)
+		{
+			TargetEntry *tle = (TargetEntry *) lfirst(l);
+			Node	   *newexpr;
+
+			/* If it's a non-Var sort/group item, first try to match by sortref */
+			if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var))
+			{
+				newexpr = (Node *)
+					search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
+														  tle->ressortgroupref,
+														  subplan_itlist,
+														  OUTER_VAR);
+				if (!newexpr)
+					newexpr = fix_combine_agg_expr(root,
+												 (Node *) tle->expr,
+												 subplan_itlist,
+												 OUTER_VAR,
+												 rtoffset);
+			}
+			else
+				newexpr = fix_combine_agg_expr(root,
+											 (Node *) tle->expr,
+											 subplan_itlist,
+											 OUTER_VAR,
+											 rtoffset);
+			tle = flatCopyTargetEntry(tle);
+			tle->expr = (Expr *) newexpr;
+			output_targetlist = lappend(output_targetlist, tle);
+		}
+	}
+	else
+	{
+		foreach(l, plan->targetlist)
+		{
+			TargetEntry *tle = (TargetEntry *) lfirst(l);
+			Node	   *newexpr;
+
+			/* If it's a non-Var sort/group item, first try to match by sortref */
+			if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var))
+			{
+				newexpr = (Node *)
+					search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
+														  tle->ressortgroupref,
+														  subplan_itlist,
+														  OUTER_VAR);
+				if (!newexpr)
+					newexpr = fix_upper_expr(root,
+											 (Node *) tle->expr,
+											 subplan_itlist,
+											 OUTER_VAR,
+											 rtoffset);
+			}
+			else
+				newexpr = fix_upper_expr(root,
+										 (Node *) tle->expr,
+										 subplan_itlist,
+										 OUTER_VAR,
+										 rtoffset);
+			tle = flatCopyTargetEntry(tle);
+			tle->expr = (Expr *) newexpr;
+			output_targetlist = lappend(output_targetlist, tle);
+		}
+	}
+
+	plan->targetlist = output_targetlist;
+
+	plan->qual = (List *)
+		fix_upper_expr(root,
+					   (Node *) plan->qual,
+					   subplan_itlist,
+					   OUTER_VAR,
+					   rtoffset);
+
+	pfree(subplan_itlist);
+}
+
+
+/*
+ * This function is only used by combineAgg to set the Var nodes as args of
+ * Aggref reference output of a Gather plan.
+ */
+static Node *
+fix_combine_agg_expr(PlannerInfo *root,
+			   Node *node,
+			   indexed_tlist *subplan_itlist,
+			   Index newvarno,
+			   int rtoffset)
+{
+	fix_upper_expr_context context;
+
+	context.root = root;
+	context.subplan_itlist = subplan_itlist;
+	context.newvarno = newvarno;
+	context.rtoffset = rtoffset;
+	return fix_combine_agg_expr_mutator(node, &context);
+}
+
+static Node *
+fix_combine_agg_expr_mutator(Node *node, fix_upper_expr_context *context)
+{
+	Var		   *newvar;
+
+	if (node == NULL)
+		return NULL;
+	if (IsA(node, Var))
+	{
+		Var		   *var = (Var *) node;
+
+		newvar = search_indexed_tlist_for_var(var,
+											  context->subplan_itlist,
+											  context->newvarno,
+											  context->rtoffset);
+		if (!newvar)
+			elog(ERROR, "variable not found in subplan target list");
+		return (Node *) newvar;
+	}
+	if (IsA(node, Aggref))
+	{
+		TargetEntry *tle;
+		Aggref		*aggref = (Aggref*)node;
+		List	    *args = NIL;
+
+		tle = tlist_member(node, context->subplan_itlist->tlist);
+		if (tle)
+		{
+			/* Found a matching subplan output expression */
+			Var		   *newvar;
+			TargetEntry *newtle;
+
+			newvar = makeVarFromTargetEntry(context->newvarno, tle);
+			newvar->varnoold = 0;	/* wasn't ever a plain Var */
+			newvar->varoattno = 0;
+			newvar->vartype = aggref->aggtranstype; /* hack hack */
+
+			/* update the args in the aggref */
+
+			/* makeTargetEntry ,always set resno to one for finialize agg */
+			newtle = makeTargetEntry((Expr*)newvar,1,NULL,false);
+			args = lappend(args,newtle);
+
+			/*
+			 * Updated the args, let the newvar refer to the right position of
+			 * the agg function in the subplan
+			 */
+			aggref->args = args;
+
+			return (Node *) aggref;
+		}
+	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
+		if (context->subplan_itlist->has_ph_vars)
+		{
+			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+													  context->subplan_itlist,
+													  context->newvarno);
+			if (newvar)
+				return (Node *) newvar;
+		}
+		/* If not supplied by input plan, evaluate the contained expr */
+		return fix_upper_expr_mutator((Node *) phv->phexpr, context);
+	}
+	if (IsA(node, Param))
+		return fix_param_node(context->root, (Param *) node);
+
+	fix_expr_common(context->root, node);
+	return expression_tree_mutator(node,
+								   fix_combine_agg_expr_mutator,
+								   (void *) context);
+}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 00f5ce3..4d3a24e 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -445,6 +445,7 @@ partial_aggregate_walker(Node *node, void *context)
 				 aggref->aggfnoid);
 		aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
 		aggcombinefn = aggform->aggcombinefn;
+		aggref->aggtranstype = aggform->aggtranstype; /* Hack hack */
 		ReleaseSysCache(aggTuple);
 
 		/* Do we have a combine function? */
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index fa93a8c..5f37f14 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -3352,6 +3352,68 @@ numeric_avg_accum(PG_FUNCTION_ARGS)
 }
 
 /*
+ * Generic combine function for numeric aggregates
+ */
+Datum
+numeric_avg_combine(PG_FUNCTION_ARGS)
+{
+	NumericAggState *state1;
+	NumericAggState *state2;
+
+	state1 = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+	state2 = PG_ARGISNULL(1) ? NULL : (NumericAggState *) PG_GETARG_POINTER(1);
+
+	if (state2 == NULL)
+		PG_RETURN_POINTER(state1);
+
+	/* manually copy all fields from state2 to state1 */
+	if (state1 == NULL)
+	{
+		MemoryContext old_context;
+
+		state1 = makeNumericAggState(fcinfo, false);
+		state1->N = state2->N;
+		state1->NaNcount = state2->NaNcount;
+		state1->maxScale = state2->maxScale;
+		state1->maxScaleCount = state2->maxScaleCount;
+
+		old_context = MemoryContextSwitchTo(state1->agg_context);
+
+		init_var(&state1->sumX);
+		set_var_from_var(&state2->sumX, &state1->sumX);
+
+		MemoryContextSwitchTo(old_context);
+
+		PG_RETURN_POINTER(state1);
+	}
+
+	if (state2->N > 0)
+	{
+		MemoryContext old_context;
+
+		state1->N += state2->N;
+		state1->NaNcount += state2->NaNcount;
+
+		if (state2->maxScale > state1->maxScale)
+			state1->maxScale = state2->maxScale;
+		else if (state2->maxScale == state1->maxScale)
+			state1->maxScale += state2->maxScale;
+
+		/* The rest of this needs to work in the aggregate context */
+		old_context = MemoryContextSwitchTo(state1->agg_context);
+
+		/* Accumulate sums */
+		add_var(&(state1->sumX), &(state2->sumX), &(state1->sumX));
+
+		if (state1->calcSumX2)
+			add_var(&(state1->sumX2), &(state2->sumX2), &(state1->sumX2));
+
+		MemoryContextSwitchTo(old_context);
+	}
+	PG_RETURN_POINTER(state1);
+}
+
+/*
  * Generic inverse transition function for numeric aggregates
  * (with or without requirement for X^2).
  */
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index b306f9b..6ea8fbb 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -145,7 +145,7 @@ DATA(insert ( 2110	n 0 float4pl		-					float4pl	-				-					-					f f 0	700		0	0
 DATA(insert ( 2111	n 0 float8pl		-					float8pl	-				-					-					f f 0	701		0	0		0	_null_ _null_ ));
 DATA(insert ( 2112	n 0 cash_pl			-					cash_pl		cash_pl			cash_mi				-					f f 0	790		0	790		0	_null_ _null_ ));
 DATA(insert ( 2113	n 0 interval_pl		-					interval_pl	interval_pl		interval_mi			-					f f 0	1186	0	1186	0	_null_ _null_ ));
-DATA(insert ( 2114	n 0 numeric_avg_accum	numeric_sum		-		numeric_avg_accum	numeric_accum_inv	numeric_sum			f f 0	2281	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2114	n 0 numeric_avg_accum	numeric_sum		numeric_avg_combine	numeric_avg_accum	numeric_accum_inv	numeric_sum			f f 0	2281	128 2281	128 _null_ _null_ ));
 
 /* max */
 DATA(insert ( 2115	n 0 int8larger		-				int8larger			-				-				-				f f 413		20		0	0		0	_null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d8640db..f328007 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2515,6 +2515,8 @@ DESCR("aggregate final function");
 DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
 DATA(insert OID = 2858 (  numeric_avg_accum    PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ ));
+DESCR("aggregate combine function");
+DATA(insert OID = 3317 (  numeric_avg_combine    PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ numeric_avg_combine _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
 DATA(insert OID = 3548 (  numeric_accum_inv    PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_accum_inv _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 60c1ca2..81dd4f8 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -256,6 +256,7 @@ typedef struct Aggref
 	Expr		xpr;
 	Oid			aggfnoid;		/* pg_proc Oid of the aggregate */
 	Oid			aggtype;		/* type Oid of result of the aggregate */
+	Oid			aggtranstype;	 /* Hack hack */
 	Oid			aggcollid;		/* OID of collation of result */
 	Oid			inputcollid;	/* OID of collation that function should use */
 	List	   *aggdirectargs;	/* direct arguments, if an ordered-set agg */
@@ -267,6 +268,7 @@ typedef struct Aggref
 	bool		aggvariadic;	/* true if variadic arguments have been
 								 * combined into an array last argument */
 	char		aggkind;		/* aggregate kind (see pg_aggregate.h) */
+	bool		aggskipfinalize;/* TRUE if Aggref should skip finalization */
 	Index		agglevelsup;	/* > 0 if agg belongs to outer query */
 	int			location;		/* token location, or -1 if unknown */
 } Aggref;
