diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 229b408..cd43416 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -259,6 +259,13 @@ typedef struct NumericVar
 	NumericDigit *digits;		/* base-NBASE digits */
 } NumericVar;
 
+/* Transition state for numeric average aggregate. */
+typedef struct AvgAggState
+{
+	Numeric 	sumX;
+	uint64 		N;
+	size_t 		sumX_size;
+} AvgAggState;
 
 /* ----------
  * Some preinitialized constants
@@ -432,7 +439,7 @@ static void trunc_var(NumericVar *var, int rscale);
 static void strip_var(NumericVar *var);
 static void compute_bucket(Numeric operand, Numeric bound1, Numeric bound2,
 			   NumericVar *count_var, NumericVar *result_var);
-
+static AvgAggState * makeAvgAggState(FunctionCallInfo fcinfo);
 
 /* ----------------------------------------------------------------------
  *
@@ -1576,6 +1583,117 @@ numeric_add(PG_FUNCTION_ARGS)
 	PG_RETURN_NUMERIC(res);
 }
 
+/*
+ * numeric_fadd() -
+ *
+ *	fast sum aggregate
+ */
+typedef struct SumAggState
+{
+	NumericVar		sumX;
+	MemoryContext		agg_context;
+	bool			isNaN;
+} SumAggState;
+
+static SumAggState *
+makeSumAggState(FunctionCallInfo fcinfo)
+{
+	SumAggState *state;
+	MemoryContext		agg_context;
+	MemoryContext		old_context;
+
+	if (!AggCheckCallContext(fcinfo, &agg_context))
+	{
+		elog(ERROR, "numeric_fadd is called in non-aggregate context");
+	}
+
+	old_context = MemoryContextSwitchTo(agg_context);
+	state = palloc0(sizeof(SumAggState));
+	state->agg_context = agg_context;
+
+	MemoryContextSwitchTo(old_context);
+
+	return state;
+}
+
+
+Datum
+numeric_fadd_accum(PG_FUNCTION_ARGS)
+{
+	SumAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (SumAggState *) PG_GETARG_POINTER(0);
+
+	if (!PG_ARGISNULL(1))
+	{
+		bool	first = false;
+
+		if (state == NULL)
+		{
+			state = makeSumAggState(fcinfo);
+			first = true;
+		}
+
+		if (!state->isNaN)
+		{
+			Numeric par = PG_GETARG_NUMERIC(1);
+
+			if (NUMERIC_IS_NAN(par))
+			{
+				state->isNaN = true;
+			}
+			else
+			{
+				NumericVar	numvar;
+				NumericVar	tmp;
+				MemoryContext		old_context;
+
+				init_var_from_num(par, &numvar);
+
+				old_context = MemoryContextSwitchTo(state->agg_context);
+
+				if (!first)
+				{
+					memcpy(&tmp, &(state->sumX), sizeof(NumericVar));
+					init_var(&(state->sumX));
+
+					add_var(&numvar, &tmp, &(state->sumX));
+					free_var(&tmp);
+				}
+				else
+					set_var_from_var(&numvar, &(state->sumX));
+
+				MemoryContextSwitchTo(old_context);
+
+				
+			}
+		}
+	}
+
+	PG_RETURN_POINTER(state);
+}
+
+Datum
+numeric_fadd(PG_FUNCTION_ARGS)
+{
+	SumAggState *state;
+	Numeric		res;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+
+	state = (SumAggState *) PG_GETARG_POINTER(0);
+	if (state->isNaN)
+	{
+		pfree(state);
+		PG_RETURN_NUMERIC(make_result(&const_nan));
+	}
+
+	res = make_result(&(state->sumX));
+	pfree(state);
+
+	PG_RETURN_NUMERIC(res);
+}
 
 /*
  * numeric_sub() -
@@ -2511,38 +2629,28 @@ do_numeric_accum(ArrayType *transarray, Numeric newval)
 	return result;
 }
 
-/*
- * Improve avg performance by not caclulating sum(X*X).
- */
-static ArrayType *
-do_numeric_avg_accum(ArrayType *transarray, Numeric newval)
+static void
+do_numeric_avg_accum(AvgAggState *state, Numeric newval)
 {
-	Datum	   *transdatums;
-	int			ndatums;
-	Datum		N,
-				sumX;
-	ArrayType  *result;
-
-	/* We assume the input is array of numeric */
-	deconstruct_array(transarray,
-					  NUMERICOID, -1, false, 'i',
-					  &transdatums, NULL, &ndatums);
-	if (ndatums != 2)
-		elog(ERROR, "expected 2-element numeric array");
-	N = transdatums[0];
-	sumX = transdatums[1];
-
-	N = DirectFunctionCall1(numeric_inc, N);
-	sumX = DirectFunctionCall2(numeric_add, sumX,
-							   NumericGetDatum(newval));
+	Numeric 	 newsumX;
+	size_t		 newsumX_size;
 
-	transdatums[0] = N;
-	transdatums[1] = sumX;
+	/* Calculate the new value for sumX. */
+	newsumX = DatumGetNumeric(DirectFunctionCall2(numeric_add, 
+							  NumericGetDatum(state->sumX),
+							  NumericGetDatum(newval)));
 
-	result = construct_array(transdatums, 2,
-							 NUMERICOID, -1, false, 'i');
+	/* Enlarge state->sumX to have enough space for the new sumX. */
+	newsumX_size = VARSIZE(newsumX);
+	if (newsumX_size > state->sumX_size)
+	{
+		state->sumX = repalloc(state->sumX, newsumX_size);
+		state->sumX_size = newsumX_size;
+	}
 
-	return result;
+	/* Update state. */
+	memcpy(state->sumX, newsumX, newsumX_size);
+	state->N++;
 }
 
 Datum
@@ -2560,10 +2668,20 @@ numeric_accum(PG_FUNCTION_ARGS)
 Datum
 numeric_avg_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Numeric		newval = PG_GETARG_NUMERIC(1);
+	AvgAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (AvgAggState *) PG_GETARG_POINTER(0);
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+	if (!PG_ARGISNULL(1))
+	{
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeAvgAggState(fcinfo);
+		
+		do_numeric_avg_accum(state, PG_GETARG_NUMERIC(1));
+	}
+
+	PG_RETURN_POINTER(state);
 }
 
 /*
@@ -2617,42 +2735,43 @@ int8_accum(PG_FUNCTION_ARGS)
 Datum
 int8_avg_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum		newval8 = PG_GETARG_DATUM(1);
-	Numeric		newval;
+	AvgAggState *state;
 
-	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+	state = PG_ARGISNULL(0) ? NULL : (AvgAggState *) PG_GETARG_POINTER(0);
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
-}
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval8;
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeAvgAggState(fcinfo);
+
+		newval8 = PG_GETARG_DATUM(1);
+		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+		
+		do_numeric_avg_accum(state, newval);
+	}
 
+	PG_RETURN_POINTER(state);
+}
 
 Datum
 numeric_avg(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum	   *transdatums;
-	int			ndatums;
-	Numeric		N,
-				sumX;
-
-	/* We assume the input is array of numeric */
-	deconstruct_array(transarray,
-					  NUMERICOID, -1, false, 'i',
-					  &transdatums, NULL, &ndatums);
-	if (ndatums != 2)
-		elog(ERROR, "expected 2-element numeric array");
-	N = DatumGetNumeric(transdatums[0]);
-	sumX = DatumGetNumeric(transdatums[1]);
+	Datum 		 countd;
+	AvgAggState *state;
 
-	/* SQL92 defines AVG of no values to be NULL */
-	/* N is zero iff no digits (cf. numeric_uminus) */
-	if (NUMERIC_NDIGITS(N) == 0)
+	if (PG_ARGISNULL(0))
 		PG_RETURN_NULL();
 
+	state = (AvgAggState *) PG_GETARG_POINTER(0);
+	countd = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N));
+
 	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div,
-										NumericGetDatum(sumX),
-										NumericGetDatum(N)));
+										NumericGetDatum(state->sumX),
+										countd));
 }
 
 /*
@@ -6170,3 +6289,39 @@ strip_var(NumericVar *var)
 	var->digits = digits;
 	var->ndigits = ndigits;
 }
+
+/*
+ * makeAvgAggState
+ *
+ * Initialize state for numeric avg aggregate in the aggregate context
+ */
+static AvgAggState * 
+makeAvgAggState(FunctionCallInfo fcinfo)
+{
+	AvgAggState 	*state;
+	NumericVar 		*sumX_var;
+	MemoryContext 	 agg_context;
+	MemoryContext 	 old_context;
+
+	if (!AggCheckCallContext(fcinfo, &agg_context))
+	{
+		/* cannot be called directly because of internal-type argument */
+		elog(ERROR, "numeric_avg_accum called in non-aggregate context");
+	}
+
+	sumX_var = palloc0(sizeof(NumericVar));
+	zero_var(sumX_var);
+
+	/*
+	 * Create state in aggregate context.  It'll stay there across subsequent
+	 * calls.
+	 */
+	old_context = MemoryContextSwitchTo(agg_context);
+	state = palloc0(sizeof(AvgAggState));
+	state->sumX = make_result(sumX_var);
+	state->sumX_size = VARSIZE(state->sumX);
+	state->N = 0;
+	MemoryContextSwitchTo(old_context);
+
+	return state;
+}
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 6fb10a9..fb12637 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -77,13 +77,14 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  */
 
 /* avg */
-DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
+DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	2281	_null_ ));
 DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
 DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
-DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0	1231	"{0,0}" ));
+DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0	2281	_null_ ));
 DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
 DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
 DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
+DATA(insert ( 3179	numeric_fadd_accum	numeric_fadd		0	2281	_null_ ));
 
 /* sum */
 DATA(insert ( 2107	int8_sum		-				0	1700	_null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c97056e..94e5d4e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2385,18 +2385,22 @@ DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f
 DESCR("aggregate final function");
 DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 1700" _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 t f i 2 0 1231 "1231 1700" _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ ));
+DATA(insert OID = 2858 (  numeric_avg_accum    PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
+DATA(insert OID = 3177 (  numeric_fadd_accum    PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ numeric_fadd_accum _null_ _null_ _null_ ));
+DESCR("fast sum aggregate transition function");
 DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 21" _null_ _null_ _null_ _null_ int2_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
 DATA(insert OID = 1835 (  int4_accum	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 23" _null_ _null_ _null_ _null_ int4_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
 DATA(insert OID = 1836 (  int8_accum	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 20" _null_ _null_ _null_ _null_ int8_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
-DATA(insert OID = 2746 (  int8_avg_accum	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1231 "1231 20" _null_ _null_ _null_ _null_ int8_avg_accum _null_ _null_ _null_ ));
+DATA(insert OID = 2746 (  int8_avg_accum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 20" _null_ _null_ _null_ _null_ int8_avg_accum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
-DATA(insert OID = 1837 (  numeric_avg	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_avg _null_ _null_ _null_ ));
+DATA(insert OID = 1837 (  numeric_avg	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_avg _null_ _null_ _null_ ));
 DESCR("aggregate final function");
+DATA(insert OID = 3188 (  numeric_fadd	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_fadd _null_ _null_ _null_ ));
+DESCR("aggregate fast sum final function");
 DATA(insert OID = 2514 (  numeric_var_pop  PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_var_pop _null_ _null_ _null_ ));
 DESCR("aggregate final function");
 DATA(insert OID = 1838 (  numeric_var_samp PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_var_samp _null_ _null_ _null_ ));
@@ -3034,7 +3038,8 @@ DATA(insert OID = 2105 (  avg				PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 701 "7
 DESCR("the average (arithmetic mean) as float8 of all float8 values");
 DATA(insert OID = 2106 (  avg				PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 1186 "1186" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
 DESCR("the average (arithmetic mean) as interval of all interval values");
-
+DATA(insert OID = 3179 (  fsum				PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 1700 "1700" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DESCR("the sum as numeric of all numeric values");
 DATA(insert OID = 2107 (  sum				PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 1700 "20" _null_ _null_ _null_ _null_	aggregate_dummy _null_ _null_ _null_ ));
 DESCR("sum as numeric across all bigint input values");
 DATA(insert OID = 2108 (  sum				PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 20 "23" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index c0debe4..d680367 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -949,6 +949,8 @@ extern Datum numeric_lt(PG_FUNCTION_ARGS);
 extern Datum numeric_le(PG_FUNCTION_ARGS);
 extern Datum numeric_add(PG_FUNCTION_ARGS);
 extern Datum numeric_sub(PG_FUNCTION_ARGS);
+extern Datum numeric_fadd_accum(PG_FUNCTION_ARGS);
+extern Datum numeric_fadd(PG_FUNCTION_ARGS);
 extern Datum numeric_mul(PG_FUNCTION_ARGS);
 extern Datum numeric_div(PG_FUNCTION_ARGS);
 extern Datum numeric_div_trunc(PG_FUNCTION_ARGS);
